/*

  OpenLayers.js -- OpenLayers Map Viewer Library

  Copyright 2005-2011 OpenLayers Contributors, released under the FreeBSD
  license. Please see http://svn.openlayers.org/trunk/openlayers/license.txt
  for the full text of the license.

  Includes compressed code under the following licenses:

  (For uncompressed versions of the code used please see the
  OpenLayers SVN repository: <http://openlayers.org/>)

 */
/* Contains portions of Prototype.js:
 *
 * Prototype JavaScript framework, version 1.4.0
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
 *--------------------------------------------------------------------------*/
/**  
 *  
 *  Contains portions of Rico <http://openrico.org/>
 * 
 *  Copyright 2005 Sabre Airline Solutions  
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License"); you
 *  may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *  
 *         http://www.apache.org/licenses/LICENSE-2.0  
 *  
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 *  implied. See the License for the specific language governing
 *  permissions and limitations under the License. 
 *
 **/
/**
 * Contains XMLHttpRequest.js <http://code.google.com/p/xmlhttprequest/>
 * Copyright 2007 Sergey Ilinsky (http://www.ilinsky.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 */
/**
 * Contains portions of Gears <http://code.google.com/apis/gears/>
 *
 * Copyright 2007, Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. Neither the name of Google Inc. nor the names of its contributors may be
 *     used to endorse or promote products derived from this software without
 *     specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Sets up google.gears.*, which is *the only* supported way to access Gears.
 *
 * Circumvent this file at your own risk!
 *
 * In the future, Gears may automatically define google.gears.* without this
 * file. Gears may use these objects to transparently fix bugs and compatibility
 * issues. Applications that use the code below will continue to work seamlessly
 * when that happens.
 */
/**
 * OpenLayers.Util.pagePosition is based on Yahoo's getXY method, which is
 * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
 * 
 * Redistribution and use of this software in source and binary forms, with or
 * without modification, are permitted provided that the following conditions
 * are met: * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer. *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution. * Neither the name of
 * Yahoo! Inc. nor the names of its contributors may be used to endorse or
 * promote products derived from this software without specific prior written
 * permission of Yahoo! Inc.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
(function () {
    if (window.google && google.gears) {
        return;
    }
    var factory = null;
    if (typeof GearsFactory != 'undefined') {
        factory = new GearsFactory();
    } else {
        try {
            factory = new ActiveXObject('Gears.Factory');
            if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
                factory.privateSetGlobalObject(this);
            }
        } catch (e) {
            if ((typeof navigator.mimeTypes != 'undefined') && navigator.mimeTypes["application/x-googlegears"]) {
                factory = document.createElement("object");
                factory.style.display = "none";
                factory.width = 0;
                factory.height = 0;
                factory.type = "application/x-googlegears";
                document.documentElement.appendChild(factory);
            }
        }
    }
    if (!factory) {
        return;
    }
    if (!window.google) {
        google = {};
    }
    if (!google.gears) {
        google.gears = {
            factory: factory
        };
    }
})();
var OpenLayers = {
    VERSION_NUMBER: "Release 2.11",
    singleFile: true,
    _getScriptLocation: (function () {
        var r = new RegExp("(^|(.*?\\/))(OpenLayers\.js)(\\?|$)"),
            s = document.getElementsByTagName('script'),
            src, m, l = "";
        for (var i = 0, len = s.length; i < len; i++) {
            src = s[i].getAttribute('src');
            if (src) {
                var m = src.match(r);
                if (m) {
                    l = m[1];
                    break;
                }
            }
        }
        return (function () {
            return l;
        });
    })()
};
OpenLayers.Class = function () {
    var len = arguments.length;
    var P = arguments[0];
    var F = arguments[len - 1];
    var C = typeof F.initialize == "function" ? F.initialize : function () {
            P.prototype.initialize.apply(this, arguments);
        };
    if (len > 1) {
        var newArgs = [C, P].concat(Array.prototype.slice.call(arguments).slice(1, len - 1), F);
        OpenLayers.inherit.apply(null, newArgs);
    } else {
        C.prototype = F;
    }
    return C;
};
OpenLayers.Class.isPrototype = function () {};
OpenLayers.Class.create = function () {
    return function () {
        if (arguments && arguments[0] != OpenLayers.Class.isPrototype) {
            this.initialize.apply(this, arguments);
        }
    };
};
OpenLayers.Class.inherit = function (P) {
    var C = function () {
            P.call(this);
        };
    var newArgs = [C].concat(Array.prototype.slice.call(arguments));
    OpenLayers.inherit.apply(null, newArgs);
    return C.prototype;
};
OpenLayers.inherit = function (C, P) {
    var F = function () {};
    F.prototype = P.prototype;
    C.prototype = new F;
    var i, l, o;
    for (i = 2, l = arguments.length; i < l; i++) {
        o = arguments[i];
        if (typeof o === "function") {
            o = o.prototype;
        }
        OpenLayers.Util.extend(C.prototype, o);
    }
};
OpenLayers.Util = OpenLayers.Util || {};
OpenLayers.Util.extend = function (destination, source) {
    destination = destination || {};
    if (source) {
        for (var property in source) {
            var value = source[property];
            if (value !== undefined) {
                destination[property] = value;
            }
        }
        var sourceIsEvt = typeof window.Event == "function" && source instanceof window.Event;
        if (!sourceIsEvt && source.hasOwnProperty && source.hasOwnProperty("toString")) {
            destination.toString = source.toString;
        }
    }
    return destination;
};
OpenLayers.Protocol = OpenLayers.Class({
    format: null,
    options: null,
    autoDestroy: true,
    defaultFilter: null,
    initialize: function (options) {
        options = options || {};
        OpenLayers.Util.extend(this, options);
        this.options = options;
    },
    mergeWithDefaultFilter: function (filter) {
        var merged;
        if (filter && this.defaultFilter) {
            merged = new OpenLayers.Filter.Logical({
                type: OpenLayers.Filter.Logical.AND,
                filters: [this.defaultFilter, filter]
            });
        } else {
            merged = filter || this.defaultFilter || undefined;
        }
        return merged;
    },
    destroy: function () {
        this.options = null;
        this.format = null;
    },
    read: function (options) {
        options = options || {};
        options.filter = this.mergeWithDefaultFilter(options.filter);
    },
    create: function () {},
    update: function () {},
    "delete": function () {},
    commit: function () {},
    abort: function (response) {},
    createCallback: function (method, response, options) {
        return OpenLayers.Function.bind(function () {
            method.apply(this, [response, options]);
        }, this);
    },
    CLASS_NAME: "OpenLayers.Protocol"
});
OpenLayers.Protocol.Response = OpenLayers.Class({
    code: null,
    requestType: null,
    last: true,
    features: null,
    reqFeatures: null,
    priv: null,
    error: null,
    initialize: function (options) {
        OpenLayers.Util.extend(this, options);
    },
    success: function () {
        return this.code > 0;
    },
    CLASS_NAME: "OpenLayers.Protocol.Response"
});
OpenLayers.Protocol.Response.SUCCESS = 1;
OpenLayers.Protocol.Response.FAILURE = 0;
OpenLayers.Protocol.SQL = OpenLayers.Class(OpenLayers.Protocol, {
    databaseName: 'ol',
    tableName: "ol_vector_features",
    postReadFiltering: true,
    initialize: function (options) {
        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
    },
    destroy: function () {
        OpenLayers.Protocol.prototype.destroy.apply(this);
    },
    supported: function () {
        return false;
    },
    evaluateFilter: function (feature, filter) {
        return filter && this.postReadFiltering ? filter.evaluate(feature) : true;
    },
    CLASS_NAME: "OpenLayers.Protocol.SQL"
});
OpenLayers.Console = {
    log: function () {},
    debug: function () {},
    info: function () {},
    warn: function () {},
    error: function () {},
    userError: function (error) {
        alert(error);
    },
    assert: function () {},
    dir: function () {},
    dirxml: function () {},
    trace: function () {},
    group: function () {},
    groupEnd: function () {},
    time: function () {},
    timeEnd: function () {},
    profile: function () {},
    profileEnd: function () {},
    count: function () {},
    CLASS_NAME: "OpenLayers.Console"
};
(function () {
    var scripts = document.getElementsByTagName("script");
    for (var i = 0, len = scripts.length; i < len; ++i) {
        if (scripts[i].src.indexOf("firebug.js") != -1) {
            if (console) {
                OpenLayers.Util.extend(OpenLayers.Console, console);
                break;
            }
        }
    }
})();
OpenLayers.Lang = {
    code: null,
    defaultCode: "en",
    getCode: function () {
        if (!OpenLayers.Lang.code) {
            OpenLayers.Lang.setCode();
        }
        return OpenLayers.Lang.code;
    },
    setCode: function (code) {
        var lang;
        if (!code) {
            code = (OpenLayers.BROWSER_NAME == "msie") ? navigator.userLanguage : navigator.language;
        }
        var parts = code.split('-');
        parts[0] = parts[0].toLowerCase();
        if (typeof OpenLayers.Lang[parts[0]] == "object") {
            lang = parts[0];
        }
        if (parts[1]) {
            var testLang = parts[0] + '-' + parts[1].toUpperCase();
            if (typeof OpenLayers.Lang[testLang] == "object") {
                lang = testLang;
            }
        }
        if (!lang) {
            OpenLayers.Console.warn('Failed to find OpenLayers.Lang.' + parts.join("-") + ' dictionary, falling back to default language');
            lang = OpenLayers.Lang.defaultCode;
        }
        OpenLayers.Lang.code = lang;
    },
    translate: function (key, context) {
        var dictionary = OpenLayers.Lang[OpenLayers.Lang.getCode()];
        var message = dictionary && dictionary[key];
        if (!message) {
            message = key;
        }
        if (context) {
            message = OpenLayers.String.format(message, context);
        }
        return message;
    }
};
OpenLayers.i18n = OpenLayers.Lang.translate;
OpenLayers.String = {
    startsWith: function (str, sub) {
        return (str.indexOf(sub) == 0);
    },
    contains: function (str, sub) {
        return (str.indexOf(sub) != -1);
    },
    trim: function (str) {
        return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
    },
    camelize: function (str) {
        var oStringList = str.split('-');
        var camelizedString = oStringList[0];
        for (var i = 1, len = oStringList.length; i < len; i++) {
            var s = oStringList[i];
            camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
        }
        return camelizedString;
    },
    format: function (template, context, args) {
        if (!context) {
            context = window;
        }
        var replacer = function (str, match) {
                var replacement;
                var subs = match.split(/\.+/);
                for (var i = 0; i < subs.length; i++) {
                    if (i == 0) {
                        replacement = context;
                    }
                    replacement = replacement[subs[i]];
                }
                if (typeof replacement == "function") {
                    replacement = args ? replacement.apply(null, args) : replacement();
                }
                if (typeof replacement == 'undefined') {
                    return 'undefined';
                } else {
                    return replacement;
                }
            };
        return template.replace(OpenLayers.String.tokenRegEx, replacer);
    },
    tokenRegEx: /\$\{([\w.]+?)\}/g,
    numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
    isNumeric: function (value) {
        return OpenLayers.String.numberRegEx.test(value);
    },
    numericIf: function (value) {
        return OpenLayers.String.isNumeric(value) ? parseFloat(value) : value;
    }
};
if (!String.prototype.startsWith) {
    String.prototype.startsWith = function (sStart) {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.String.startsWith'
        }));
        return OpenLayers.String.startsWith(this, sStart);
    };
}
if (!String.prototype.contains) {
    String.prototype.contains = function (str) {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.String.contains'
        }));
        return OpenLayers.String.contains(this, str);
    };
}
if (!String.prototype.trim) {
    String.prototype.trim = function () {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.String.trim'
        }));
        return OpenLayers.String.trim(this);
    };
}
if (!String.prototype.camelize) {
    String.prototype.camelize = function () {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.String.camelize'
        }));
        return OpenLayers.String.camelize(this);
    };
}
OpenLayers.Number = {
    decimalSeparator: ".",
    thousandsSeparator: ",",
    limitSigDigs: function (num, sig) {
        var fig = 0;
        if (sig > 0) {
            fig = parseFloat(num.toPrecision(sig));
        }
        return fig;
    },
    format: function (num, dec, tsep, dsep) {
        dec = (typeof dec != "undefined") ? dec : 0;
        tsep = (typeof tsep != "undefined") ? tsep : OpenLayers.Number.thousandsSeparator;
        dsep = (typeof dsep != "undefined") ? dsep : OpenLayers.Number.decimalSeparator;
        if (dec != null) {
            num = parseFloat(num.toFixed(dec));
        }
        var parts = num.toString().split(".");
        if (parts.length == 1 && dec == null) {
            dec = 0;
        }
        var integer = parts[0];
        if (tsep) {
            var thousands = /(-?[0-9]+)([0-9]{3})/;
            while (thousands.test(integer)) {
                integer = integer.replace(thousands, "$1" + tsep + "$2");
            }
        }
        var str;
        if (dec == 0) {
            str = integer;
        } else {
            var rem = parts.length > 1 ? parts[1] : "0";
            if (dec != null) {
                rem = rem + new Array(dec - rem.length + 1).join("0");
            }
            str = integer + dsep + rem;
        }
        return str;
    }
};
if (!Number.prototype.limitSigDigs) {
    Number.prototype.limitSigDigs = function (sig) {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.Number.limitSigDigs'
        }));
        return OpenLayers.Number.limitSigDigs(this, sig);
    };
}
OpenLayers.Function = {
    bind: function (func, object) {
        var args = Array.prototype.slice.apply(arguments, [2]);
        return function () {
            var newArgs = args.concat(Array.prototype.slice.apply(arguments, [0]));
            return func.apply(object, newArgs);
        };
    },
    bindAsEventListener: function (func, object) {
        return function (event) {
            return func.call(object, event || window.event);
        };
    },
    False: function () {
        return false;
    },
    True: function () {
        return true;
    },
    Void: function () {}
};
if (!Function.prototype.bind) {
    Function.prototype.bind = function () {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.Function.bind'
        }));
        Array.prototype.unshift.apply(arguments, [this]);
        return OpenLayers.Function.bind.apply(null, arguments);
    };
}
if (!Function.prototype.bindAsEventListener) {
    Function.prototype.bindAsEventListener = function (object) {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            'newMethod': 'OpenLayers.Function.bindAsEventListener'
        }));
        return OpenLayers.Function.bindAsEventListener(this, object);
    };
}
OpenLayers.Array = {
    filter: function (array, callback, caller) {
        var selected = [];
        if (Array.prototype.filter) {
            selected = array.filter(callback, caller);
        } else {
            var len = array.length;
            if (typeof callback != "function") {
                throw new TypeError();
            }
            for (var i = 0; i < len; i++) {
                if (i in array) {
                    var val = array[i];
                    if (callback.call(caller, val, i, array)) {
                        selected.push(val);
                    }
                }
            }
        }
        return selected;
    }
};
OpenLayers.Bounds = OpenLayers.Class({
    left: null,
    bottom: null,
    right: null,
    top: null,
    centerLonLat: null,
    initialize: function (left, bottom, right, top) {
        if (left != null) {
            this.left = OpenLayers.Util.toFloat(left);
        }
        if (bottom != null) {
            this.bottom = OpenLayers.Util.toFloat(bottom);
        }
        if (right != null) {
            this.right = OpenLayers.Util.toFloat(right);
        }
        if (top != null) {
            this.top = OpenLayers.Util.toFloat(top);
        }
    },
    clone: function () {
        return new OpenLayers.Bounds(this.left, this.bottom, this.right, this.top);
    },
    equals: function (bounds) {
        var equals = false;
        if (bounds != null) {
            equals = ((this.left == bounds.left) && (this.right == bounds.right) && (this.top == bounds.top) && (this.bottom == bounds.bottom));
        }
        return equals;
    },
    toString: function () {
        return [this.left, this.bottom, this.right, this.top].join(",");
    },
    toArray: function (reverseAxisOrder) {
        if (reverseAxisOrder === true) {
            return [this.bottom, this.left, this.top, this.right];
        } else {
            return [this.left, this.bottom, this.right, this.top];
        }
    },
    toBBOX: function (decimal, reverseAxisOrder) {
        if (decimal == null) {
            decimal = 6;
        }
        var mult = Math.pow(10, decimal);
        var xmin = Math.round(this.left * mult) / mult;
        var ymin = Math.round(this.bottom * mult) / mult;
        var xmax = Math.round(this.right * mult) / mult;
        var ymax = Math.round(this.top * mult) / mult;
        if (reverseAxisOrder === true) {
            return ymin + "," + xmin + "," + ymax + "," + xmax;
        } else {
            return xmin + "," + ymin + "," + xmax + "," + ymax;
        }
    },
    toGeometry: function () {
        return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(this.left, this.bottom), new OpenLayers.Geometry.Point(this.right, this.bottom), new OpenLayers.Geometry.Point(this.right, this.top), new OpenLayers.Geometry.Point(this.left, this.top)])]);
    },
    getWidth: function () {
        return (this.right - this.left);
    },
    getHeight: function () {
        return (this.top - this.bottom);
    },
    getSize: function () {
        return new OpenLayers.Size(this.getWidth(), this.getHeight());
    },
    getCenterPixel: function () {
        return new OpenLayers.Pixel((this.left + this.right) / 2, (this.bottom + this.top) / 2);
    },
    getCenterLonLat: function () {
        if (!this.centerLonLat) {
            this.centerLonLat = new OpenLayers.LonLat((this.left + this.right) / 2, (this.bottom + this.top) / 2);
        }
        return this.centerLonLat;
    },
    scale: function (ratio, origin) {
        if (origin == null) {
            origin = this.getCenterLonLat();
        }
        var origx, origy;
        if (origin.CLASS_NAME == "OpenLayers.LonLat") {
            origx = origin.lon;
            origy = origin.lat;
        } else {
            origx = origin.x;
            origy = origin.y;
        }
        var left = (this.left - origx) * ratio + origx;
        var bottom = (this.bottom - origy) * ratio + origy;
        var right = (this.right - origx) * ratio + origx;
        var top = (this.top - origy) * ratio + origy;
        return new OpenLayers.Bounds(left, bottom, right, top);
    },
    add: function (x, y) {
        if ((x == null) || (y == null)) {
            var msg = OpenLayers.i18n("boundsAddError");
            OpenLayers.Console.error(msg);
            return null;
        }
        return new OpenLayers.Bounds(this.left + x, this.bottom + y, this.right + x, this.top + y);
    },
    extend: function (object) {
        var bounds = null;
        if (object) {
            switch (object.CLASS_NAME) {
            case "OpenLayers.LonLat":
                bounds = new OpenLayers.Bounds(object.lon, object.lat, object.lon, object.lat);
                break;
            case "OpenLayers.Geometry.Point":
                bounds = new OpenLayers.Bounds(object.x, object.y, object.x, object.y);
                break;
            case "OpenLayers.Bounds":
                bounds = object;
                break;
            }
            if (bounds) {
                this.centerLonLat = null;
                if ((this.left == null) || (bounds.left < this.left)) {
                    this.left = bounds.left;
                }
                if ((this.bottom == null) || (bounds.bottom < this.bottom)) {
                    this.bottom = bounds.bottom;
                }
                if ((this.right == null) || (bounds.right > this.right)) {
                    this.right = bounds.right;
                }
                if ((this.top == null) || (bounds.top > this.top)) {
                    this.top = bounds.top;
                }
            }
        }
    },
    containsLonLat: function (ll, inclusive) {
        return this.contains(ll.lon, ll.lat, inclusive);
    },
    containsPixel: function (px, inclusive) {
        return this.contains(px.x, px.y, inclusive);
    },
    contains: function (x, y, inclusive) {
        if (inclusive == null) {
            inclusive = true;
        }
        if (x == null || y == null) {
            return false;
        }
        x = OpenLayers.Util.toFloat(x);
        y = OpenLayers.Util.toFloat(y);
        var contains = false;
        if (inclusive) {
            contains = ((x >= this.left) && (x <= this.right) && (y >= this.bottom) && (y <= this.top));
        } else {
            contains = ((x > this.left) && (x < this.right) && (y > this.bottom) && (y < this.top));
        }
        return contains;
    },
    intersectsBounds: function (bounds, inclusive) {
        if (inclusive == null) {
            inclusive = true;
        }
        var intersects = false;
        var mightTouch = (this.left == bounds.right || this.right == bounds.left || this.top == bounds.bottom || this.bottom == bounds.top);
        if (inclusive || !mightTouch) {
            var inBottom = (((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) || ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top)));
            var inTop = (((bounds.top >= this.bottom) && (bounds.top <= this.top)) || ((this.top > bounds.bottom) && (this.top < bounds.top)));
            var inLeft = (((bounds.left >= this.left) && (bounds.left <= this.right)) || ((this.left >= bounds.left) && (this.left <= bounds.right)));
            var inRight = (((bounds.right >= this.left) && (bounds.right <= this.right)) || ((this.right >= bounds.left) && (this.right <= bounds.right)));
            intersects = ((inBottom || inTop) && (inLeft || inRight));
        }
        return intersects;
    },
    containsBounds: function (bounds, partial, inclusive) {
        if (partial == null) {
            partial = false;
        }
        if (inclusive == null) {
            inclusive = true;
        }
        var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
        var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
        var topLeft = this.contains(bounds.left, bounds.top, inclusive);
        var topRight = this.contains(bounds.right, bounds.top, inclusive);
        return (partial) ? (bottomLeft || bottomRight || topLeft || topRight) : (bottomLeft && bottomRight && topLeft && topRight);
    },
    determineQuadrant: function (lonlat) {
        var quadrant = "";
        var center = this.getCenterLonLat();
        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
        return quadrant;
    },
    transform: function (source, dest) {
        this.centerLonLat = null;
        var ll = OpenLayers.Projection.transform({
            'x': this.left,
            'y': this.bottom
        }, source, dest);
        var lr = OpenLayers.Projection.transform({
            'x': this.right,
            'y': this.bottom
        }, source, dest);
        var ul = OpenLayers.Projection.transform({
            'x': this.left,
            'y': this.top
        }, source, dest);
        var ur = OpenLayers.Projection.transform({
            'x': this.right,
            'y': this.top
        }, source, dest);
        this.left = Math.min(ll.x, ul.x);
        this.bottom = Math.min(ll.y, lr.y);
        this.right = Math.max(lr.x, ur.x);
        this.top = Math.max(ul.y, ur.y);
        return this;
    },
    wrapDateLine: function (maxExtent, options) {
        options = options || {};
        var leftTolerance = options.leftTolerance || 0;
        var rightTolerance = options.rightTolerance || 0;
        var newBounds = this.clone();
        if (maxExtent) {
            while (newBounds.left < maxExtent.left && (newBounds.right - rightTolerance) <= maxExtent.left) {
                newBounds = newBounds.add(maxExtent.getWidth(), 0);
            }
            while ((newBounds.left + leftTolerance) >= maxExtent.right && newBounds.right > maxExtent.right) {
                newBounds = newBounds.add(-maxExtent.getWidth(), 0);
            }
        }
        return newBounds;
    },
    CLASS_NAME: "OpenLayers.Bounds"
});
OpenLayers.Bounds.fromString = function (str, reverseAxisOrder) {
    var bounds = str.split(",");
    return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
};
OpenLayers.Bounds.fromArray = function (bbox, reverseAxisOrder) {
    return reverseAxisOrder === true ? new OpenLayers.Bounds(parseFloat(bbox[1]), parseFloat(bbox[0]), parseFloat(bbox[3]), parseFloat(bbox[2])) : new OpenLayers.Bounds(parseFloat(bbox[0]), parseFloat(bbox[1]), parseFloat(bbox[2]), parseFloat(bbox[3]));
};
OpenLayers.Bounds.fromSize = function (size) {
    return new OpenLayers.Bounds(0, size.h, size.w, 0);
};
OpenLayers.Bounds.oppositeQuadrant = function (quadrant) {
    var opp = "";
    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
    return opp;
};
OpenLayers.Element = {
    visible: function (element) {
        return OpenLayers.Util.getElement(element).style.display != 'none';
    },
    toggle: function () {
        for (var i = 0, len = arguments.length; i < len; i++) {
            var element = OpenLayers.Util.getElement(arguments[i]);
            var display = OpenLayers.Element.visible(element) ? 'hide' : 'show';
            OpenLayers.Element[display](element);
        }
    },
    hide: function () {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            newMethod: "element.style.display = 'none';"
        }));
        for (var i = 0, len = arguments.length; i < len; i++) {
            var element = OpenLayers.Util.getElement(arguments[i]);
            if (element) {
                element.style.display = 'none';
            }
        }
    },
    show: function () {
        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
            newMethod: "element.style.display = '';"
        }));
        for (var i = 0, len = arguments.length; i < len; i++) {
            var element = OpenLayers.Util.getElement(arguments[i]);
            if (element) {
                element.style.display = '';
            }
        }
    },
    remove: function (element) {
        element = OpenLayers.Util.getElement(element);
        element.parentNode.removeChild(element);
    },
    getHeight: function (element) {
        element = OpenLayers.Util.getElement(element);
        return element.offsetHeight;
    },
    getDimensions: function (element) {
        element = OpenLayers.Util.getElement(element);
        if (OpenLayers.Element.getStyle(element, 'display') != 'none') {
            return {
                width: element.offsetWidth,
                height: element.offsetHeight
            };
        }
        var els = element.style;
        var originalVisibility = els.visibility;
        var originalPosition = els.position;
        var originalDisplay = els.display;
        els.visibility = 'hidden';
        els.position = 'absolute';
        els.display = '';
        var originalWidth = element.clientWidth;
        var originalHeight = element.clientHeight;
        els.display = originalDisplay;
        els.position = originalPosition;
        els.visibility = originalVisibility;
        return {
            width: originalWidth,
            height: originalHeight
        };
    },
    hasClass: function (element, name) {
        var names = element.className;
        return ( !! names && new RegExp("(^|\\s)" + name + "(\\s|$)").test(names));
    },
    addClass: function (element, name) {
        if (!OpenLayers.Element.hasClass(element, name)) {
            element.className += (element.className ? " " : "") + name;
        }
        return element;
    },
    removeClass: function (element, name) {
        var names = element.className;
        if (names) {
            element.className = OpenLayers.String.trim(names.replace(new RegExp("(^|\\s+)" + name + "(\\s+|$)"), " "));
        }
        return element;
    },
    toggleClass: function (element, name) {
        if (OpenLayers.Element.hasClass(element, name)) {
            OpenLayers.Element.removeClass(element, name);
        } else {
            OpenLayers.Element.addClass(element, name);
        }
        return element;
    },
    getStyle: function (element, style) {
        element = OpenLayers.Util.getElement(element);
        var value = null;
        if (element && element.style) {
            value = element.style[OpenLayers.String.camelize(style)];
            if (!value) {
                if (document.defaultView && document.defaultView.getComputedStyle) {
                    var css = document.defaultView.getComputedStyle(element, null);
                    value = css ? css.getPropertyValue(style) : null;
                } else if (element.currentStyle) {
                    value = element.currentStyle[OpenLayers.String.camelize(style)];
                }
            }
            var positions = ['left', 'top', 'right', 'bottom'];
            if (window.opera && (OpenLayers.Util.indexOf(positions, style) != -1) && (OpenLayers.Element.getStyle(element, 'position') == 'static')) {
                value = 'auto';
            }
        }
        return value == 'auto' ? null : value;
    }
};
OpenLayers.LonLat = OpenLayers.Class({
    lon: 0.0,
    lat: 0.0,
    initialize: function (lon, lat) {
        this.lon = OpenLayers.Util.toFloat(lon);
        this.lat = OpenLayers.Util.toFloat(lat);
    },
    toString: function () {
        return ("lon=" + this.lon + ",lat=" + this.lat);
    },
    toShortString: function () {
        return (this.lon + ", " + this.lat);
    },
    clone: function () {
        return new OpenLayers.LonLat(this.lon, this.lat);
    },
    add: function (lon, lat) {
        if ((lon == null) || (lat == null)) {
            var msg = OpenLayers.i18n("lonlatAddError");
            OpenLayers.Console.error(msg);
            return null;
        }
        return new OpenLayers.LonLat(this.lon + OpenLayers.Util.toFloat(lon), this.lat + OpenLayers.Util.toFloat(lat));
    },
    equals: function (ll) {
        var equals = false;
        if (ll != null) {
            equals = ((this.lon == ll.lon && this.lat == ll.lat) || (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
        }
        return equals;
    },
    transform: function (source, dest) {
        var point = OpenLayers.Projection.transform({
            'x': this.lon,
            'y': this.lat
        }, source, dest);
        this.lon = point.x;
        this.lat = point.y;
        return this;
    },
    wrapDateLine: function (maxExtent) {
        var newLonLat = this.clone();
        if (maxExtent) {
            while (newLonLat.lon < maxExtent.left) {
                newLonLat.lon += maxExtent.getWidth();
            }
            while (newLonLat.lon > maxExtent.right) {
                newLonLat.lon -= maxExtent.getWidth();
            }
        }
        return newLonLat;
    },
    CLASS_NAME: "OpenLayers.LonLat"
});
OpenLayers.LonLat.fromString = function (str) {
    var pair = str.split(",");
    return new OpenLayers.LonLat(pair[0], pair[1]);
};
OpenLayers.LonLat.fromArray = function (arr) {
    var gotArr = OpenLayers.Util.isArray(arr),
        lon = gotArr && arr[0],
        lat = gotArr && arr[1];
    return new OpenLayers.LonLat(lon, lat);
};
OpenLayers.Pixel = OpenLayers.Class({
    x: 0.0,
    y: 0.0,
    initialize: function (x, y) {
        this.x = parseFloat(x);
        this.y = parseFloat(y);
    },
    toString: function () {
        return ("x=" + this.x + ",y=" + this.y);
    },
    clone: function () {
        return new OpenLayers.Pixel(this.x, this.y);
    },
    equals: function (px) {
        var equals = false;
        if (px != null) {
            equals = ((this.x == px.x && this.y == px.y) || (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
        }
        return equals;
    },
    distanceTo: function (px) {
        return Math.sqrt(Math.pow(this.x - px.x, 2) + Math.pow(this.y - px.y, 2));
    },
    add: function (x, y) {
        if ((x == null) || (y == null)) {
            var msg = OpenLayers.i18n("pixelAddError");
            OpenLayers.Console.error(msg);
            return null;
        }
        return new OpenLayers.Pixel(this.x + x, this.y + y);
    },
    offset: function (px) {
        var newPx = this.clone();
        if (px) {
            newPx = this.add(px.x, px.y);
        }
        return newPx;
    },
    CLASS_NAME: "OpenLayers.Pixel"
});
OpenLayers.Size = OpenLayers.Class({
    w: 0.0,
    h: 0.0,
    initialize: function (w, h) {
        this.w = parseFloat(w);
        this.h = parseFloat(h);
    },
    toString: function () {
        return ("w=" + this.w + ",h=" + this.h);
    },
    clone: function () {
        return new OpenLayers.Size(this.w, this.h);
    },
    equals: function (sz) {
        var equals = false;
        if (sz != null) {
            equals = ((this.w == sz.w && this.h == sz.h) || (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
        }
        return equals;
    },
    CLASS_NAME: "OpenLayers.Size"
});
OpenLayers.Util = OpenLayers.Util || {};
OpenLayers.Util.getElement = function () {
    var elements = [];
    for (var i = 0, len = arguments.length; i < len; i++) {
        var element = arguments[i];
        if (typeof element == 'string') {
            element = document.getElementById(element);
        }
        if (arguments.length == 1) {
            return element;
        }
        elements.push(element);
    }
    return elements;
};
OpenLayers.Util.isElement = function (o) {
    return !!(o && o.nodeType === 1);
};
OpenLayers.Util.isArray = function (a) {
    return (Object.prototype.toString.call(a) === '[object Array]');
};
if (typeof window.$ === "undefined") {
    window.$ = OpenLayers.Util.getElement;
}
OpenLayers.Util.removeItem = function (array, item) {
    for (var i = array.length - 1; i >= 0; i--) {
        if (array[i] == item) {
            array.splice(i, 1);
        }
    }
    return array;
};
OpenLayers.Util.clearArray = function (array) {
    OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
        'newMethod': 'array = []'
    }));
    array.length = 0;
};
OpenLayers.Util.indexOf = function (array, obj) {
    if (typeof array.indexOf == "function") {
        return array.indexOf(obj);
    } else {
        for (var i = 0, len = array.length; i < len; i++) {
            if (array[i] == obj) {
                return i;
            }
        }
        return -1;
    }
};
OpenLayers.Util.modifyDOMElement = function (element, id, px, sz, position, border, overflow, opacity) {
    if (id) {
        element.id = id;
    }
    if (px) {
        element.style.left = px.x + "px";
        element.style.top = px.y + "px";
    }
    if (sz) {
        element.style.width = sz.w + "px";
        element.style.height = sz.h + "px";
    }
    if (position) {
        element.style.position = position;
    }
    if (border) {
        element.style.border = border;
    }
    if (overflow) {
        element.style.overflow = overflow;
    }
    if (parseFloat(opacity) >= 0.0 && parseFloat(opacity) < 1.0) {
        element.style.filter = 'alpha(opacity=' + (opacity * 100) + ')';
        element.style.opacity = opacity;
    } else if (parseFloat(opacity) == 1.0) {
        element.style.filter = '';
        element.style.opacity = '';
    }
};
OpenLayers.Util.createDiv = function (id, px, sz, imgURL, position, border, overflow, opacity) {
    var dom = document.createElement('div');
    if (imgURL) {
        dom.style.backgroundImage = 'url(' + imgURL + ')';
    }
    if (!id) {
        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
    }
    if (!position) {
        position = "absolute";
    }
    OpenLayers.Util.modifyDOMElement(dom, id, px, sz, position, border, overflow, opacity);
    return dom;
};
OpenLayers.Util.createImage = function (id, px, sz, imgURL, position, border, opacity, delayDisplay) {
    var image = document.createElement("img");
    if (!id) {
        id = OpenLayers.Util.createUniqueID("OpenLayersDiv");
    }
    if (!position) {
        position = "relative";
    }
    OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, border, null, opacity);
    if (delayDisplay) {
        image.style.display = "none";
        OpenLayers.Event.observe(image, "load", OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, image));
        OpenLayers.Event.observe(image, "error", OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, image));
    }
    image.style.alt = id;
    image.galleryImg = "no";
    if (imgURL) {
        image.src = imgURL;
    }
    return image;
};
OpenLayers.Util.setOpacity = function (element, opacity) {
    OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity);
};
OpenLayers.Util.onImageLoad = function () {
    if (!this.viewRequestID || (this.map && this.viewRequestID == this.map.viewRequestID)) {
        this.style.display = "";
    }
    OpenLayers.Element.removeClass(this, "olImageLoadError");
};
OpenLayers.IMAGE_RELOAD_ATTEMPTS = 0;
OpenLayers.Util.onImageLoadError = function () {
    this._attempts = (this._attempts) ? (this._attempts + 1) : 1;
    if (this._attempts <= OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
        var urls = this.urls;
        if (urls && OpenLayers.Util.isArray(urls) && urls.length > 1) {
            var src = this.src.toString();
            var current_url, k;
            for (k = 0; current_url = urls[k]; k++) {
                if (src.indexOf(current_url) != -1) {
                    break;
                }
            }
            var guess = Math.floor(urls.length * Math.random());
            var new_url = urls[guess];
            k = 0;
            while (new_url == current_url && k++ < 4) {
                guess = Math.floor(urls.length * Math.random());
                new_url = urls[guess];
            }
            this.src = src.replace(current_url, new_url);
        } else {
            this.src = this.src;
        }
    } else {
        OpenLayers.Element.addClass(this, "olImageLoadError");
    }
    this.style.display = "";
};
OpenLayers.Util.alphaHackNeeded = null;
OpenLayers.Util.alphaHack = function () {
    if (OpenLayers.Util.alphaHackNeeded == null) {
        var arVersion = navigator.appVersion.split("MSIE");
        var version = parseFloat(arVersion[1]);
        var filter = false;
        try {
            filter = !! (document.body.filters);
        } catch (e) {}
        OpenLayers.Util.alphaHackNeeded = (filter && (version >= 5.5) && (version < 7));
    }
    return OpenLayers.Util.alphaHackNeeded;
};
OpenLayers.Util.modifyAlphaImageDiv = function (div, id, px, sz, imgURL, position, border, sizing, opacity) {
    OpenLayers.Util.modifyDOMElement(div, id, px, sz, position, null, null, opacity);
    var img = div.childNodes[0];
    if (imgURL) {
        img.src = imgURL;
    }
    OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, "relative", border);
    if (OpenLayers.Util.alphaHack()) {
        if (div.style.display != "none") {
            div.style.display = "inline-block";
        }
        if (sizing == null) {
            sizing = "scale";
        }
        div.style.filter = "progid:DXImageTransform.Microsoft" + ".AlphaImageLoader(src='" + img.src + "', " + "sizingMethod='" + sizing + "')";
        if (parseFloat(div.style.opacity) >= 0.0 && parseFloat(div.style.opacity) < 1.0) {
            div.style.filter += " alpha(opacity=" + div.style.opacity * 100 + ")";
        }
        img.style.filter = "alpha(opacity=0)";
    }
};
OpenLayers.Util.createAlphaImageDiv = function (id, px, sz, imgURL, position, border, sizing, opacity, delayDisplay) {
    var div = OpenLayers.Util.createDiv();
    var img = OpenLayers.Util.createImage(null, null, null, null, null, null, null, false);
    div.appendChild(img);
    if (delayDisplay) {
        img.style.display = "none";
        OpenLayers.Event.observe(img, "load", OpenLayers.Function.bind(OpenLayers.Util.onImageLoad, div));
        OpenLayers.Event.observe(img, "error", OpenLayers.Function.bind(OpenLayers.Util.onImageLoadError, div));
    }
    OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, position, border, sizing, opacity);
    return div;
};
OpenLayers.Util.upperCaseObject = function (object) {
    var uObject = {};
    for (var key in object) {
        uObject[key.toUpperCase()] = object[key];
    }
    return uObject;
};
OpenLayers.Util.applyDefaults = function (to, from) {
    to = to || {};
    var fromIsEvt = typeof window.Event == "function" && from instanceof window.Event;
    for (var key in from) {
        if (to[key] === undefined || (!fromIsEvt && from.hasOwnProperty && from.hasOwnProperty(key) && !to.hasOwnProperty(key))) {
            to[key] = from[key];
        }
    }
    if (!fromIsEvt && from && from.hasOwnProperty && from.hasOwnProperty('toString') && !to.hasOwnProperty('toString')) {
        to.toString = from.toString;
    }
    return to;
};
OpenLayers.Util.getParameterString = function (params) {
    var paramsArray = [];
    for (var key in params) {
        var value = params[key];
        if ((value != null) && (typeof value != 'function')) {
            var encodedValue;
            if (typeof value == 'object' && value.constructor == Array) {
                var encodedItemArray = [];
                var item;
                for (var itemIndex = 0, len = value.length; itemIndex < len; itemIndex++) {
                    item = value[itemIndex];
                    encodedItemArray.push(encodeURIComponent((item === null || item === undefined) ? "" : item));
                }
                encodedValue = encodedItemArray.join(",");
            } else {
                encodedValue = encodeURIComponent(value);
            }
            paramsArray.push(encodeURIComponent(key) + "=" + encodedValue);
        }
    }
    return paramsArray.join("&");
};
OpenLayers.Util.urlAppend = function (url, paramStr) {
    var newUrl = url;
    if (paramStr) {
        var parts = (url + " ").split(/[?&]/);
        newUrl += (parts.pop() === " " ? paramStr : parts.length ? "&" + paramStr : "?" + paramStr);
    }
    return newUrl;
};
OpenLayers.ImgPath = '';
OpenLayers.Util.getImagesLocation = function () {
    return OpenLayers.ImgPath || (OpenLayers._getScriptLocation() + "img/");
};
OpenLayers.Util.Try = function () {
    var returnValue = null;
    for (var i = 0, len = arguments.length; i < len; i++) {
        var lambda = arguments[i];
        try {
            returnValue = lambda();
            break;
        } catch (e) {}
    }
    return returnValue;
};
OpenLayers.Util.getXmlNodeValue = function (node) {
    var val = null;
    OpenLayers.Util.Try(function () {
        val = node.text;
        if (!val) {
            val = node.textContent;
        }
        if (!val) {
            val = node.firstChild.nodeValue;
        }
    }, function () {
        val = node.textContent;
    });
    return val;
};
OpenLayers.Util.mouseLeft = function (evt, div) {
    var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
    while (target != div && target != null) {
        target = target.parentNode;
    }
    return (target != div);
};
OpenLayers.Util.DEFAULT_PRECISION = 14;
OpenLayers.Util.toFloat = function (number, precision) {
    if (precision == null) {
        precision = OpenLayers.Util.DEFAULT_PRECISION;
    }
    if (typeof number !== "number") {
        number = parseFloat(number);
    }
    return precision === 0 ? number : parseFloat(number.toPrecision(precision));
};
OpenLayers.Util.rad = function (x) {
    return x * Math.PI / 180;
};
OpenLayers.Util.deg = function (x) {
    return x * 180 / Math.PI;
};
OpenLayers.Util.VincentyConstants = {
    a: 6378137,
    b: 6356752.3142,
    f: 1 / 298.257223563
};
OpenLayers.Util.distVincenty = function (p1, p2) {
    var ct = OpenLayers.Util.VincentyConstants;
    var a = ct.a,
        b = ct.b,
        f = ct.f;
    var L = OpenLayers.Util.rad(p2.lon - p1.lon);
    var U1 = Math.atan((1 - f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
    var U2 = Math.atan((1 - f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
    var sinU1 = Math.sin(U1),
        cosU1 = Math.cos(U1);
    var sinU2 = Math.sin(U2),
        cosU2 = Math.cos(U2);
    var lambda = L,
        lambdaP = 2 * Math.PI;
    var iterLimit = 20;
    while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0) {
        var sinLambda = Math.sin(lambda),
            cosLambda = Math.cos(lambda);
        var sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
        if (sinSigma == 0) {
            return 0;
        }
        var cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
        var sigma = Math.atan2(sinSigma, cosSigma);
        var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
        var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
        var cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
        var C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
        lambdaP = lambda;
        lambda = L + (1 - C) * f * Math.sin(alpha) * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
    }
    if (iterLimit == 0) {
        return NaN;
    }
    var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
    var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
    var s = b * A * (sigma - deltaSigma);
    var d = s.toFixed(3) / 1000;
    return d;
};
OpenLayers.Util.destinationVincenty = function (lonlat, brng, dist) {
    var u = OpenLayers.Util;
    var ct = u.VincentyConstants;
    var a = ct.a,
        b = ct.b,
        f = ct.f;
    var lon1 = lonlat.lon;
    var lat1 = lonlat.lat;
    var s = dist;
    var alpha1 = u.rad(brng);
    var sinAlpha1 = Math.sin(alpha1);
    var cosAlpha1 = Math.cos(alpha1);
    var tanU1 = (1 - f) * Math.tan(u.rad(lat1));
    var cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)),
        sinU1 = tanU1 * cosU1;
    var sigma1 = Math.atan2(tanU1, cosAlpha1);
    var sinAlpha = cosU1 * sinAlpha1;
    var cosSqAlpha = 1 - sinAlpha * sinAlpha;
    var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
    var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
    var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
    var sigma = s / (b * A),
        sigmaP = 2 * Math.PI;
    while (Math.abs(sigma - sigmaP) > 1e-12) {
        var cos2SigmaM = Math.cos(2 * sigma1 + sigma);
        var sinSigma = Math.sin(sigma);
        var cosSigma = Math.cos(sigma);
        var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
        sigmaP = sigma;
        sigma = s / (b * A) + deltaSigma;
    }
    var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1;
    var lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp));
    var lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1);
    var C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
    var L = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
    var revAz = Math.atan2(sinAlpha, -tmp);
    return new OpenLayers.LonLat(lon1 + u.deg(L), u.deg(lat2));
};
OpenLayers.Util.getParameters = function (url) {
    url = (url === null || url === undefined) ? window.location.href : url;
    var paramsString = "";
    if (OpenLayers.String.contains(url, '?')) {
        var start = url.indexOf('?') + 1;
        var end = OpenLayers.String.contains(url, "#") ? url.indexOf('#') : url.length;
        paramsString = url.substring(start, end);
    }
    var parameters = {};
    var pairs = paramsString.split(/[&;]/);
    for (var i = 0, len = pairs.length; i < len; ++i) {
        var keyValue = pairs[i].split('=');
        if (keyValue[0]) {
            var key = keyValue[0];
            try {
                key = decodeURIComponent(key);
            } catch (err) {
                key = unescape(key);
            }
            var value = (keyValue[1] || '').replace(/\+/g, " ");
            try {
                value = decodeURIComponent(value);
            } catch (err) {
                value = unescape(value);
            }
            value = value.split(",");
            if (value.length == 1) {
                value = value[0];
            }
            parameters[key] = value;
        }
    }
    return parameters;
};
OpenLayers.Util.getArgs = function (url) {
    OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
        'newMethod': 'OpenLayers.Util.getParameters'
    }));
    return OpenLayers.Util.getParameters(url);
};
OpenLayers.Util.lastSeqID = 0;
OpenLayers.Util.createUniqueID = function (prefix) {
    if (prefix == null) {
        prefix = "id_";
    }
    OpenLayers.Util.lastSeqID += 1;
    return prefix + OpenLayers.Util.lastSeqID;
};
OpenLayers.INCHES_PER_UNIT = {
    'inches': 1.0,
    'ft': 12.0,
    'mi': 63360.0,
    'm': 39.3701,
    'km': 39370.1,
    'dd': 4374754,
    'yd': 36
};
OpenLayers.INCHES_PER_UNIT["in"] = OpenLayers.INCHES_PER_UNIT.inches;
OpenLayers.INCHES_PER_UNIT["degrees"] = OpenLayers.INCHES_PER_UNIT.dd;
OpenLayers.INCHES_PER_UNIT["nmi"] = 1852 * OpenLayers.INCHES_PER_UNIT.m;
OpenLayers.METERS_PER_INCH = 0.02540005080010160020;
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
    "Inch": OpenLayers.INCHES_PER_UNIT.inches,
    "Meter": 1.0 / OpenLayers.METERS_PER_INCH,
    "Foot": 0.30480060960121920243 / OpenLayers.METERS_PER_INCH,
    "IFoot": 0.30480000000000000000 / OpenLayers.METERS_PER_INCH,
    "ClarkeFoot": 0.3047972651151 / OpenLayers.METERS_PER_INCH,
    "SearsFoot": 0.30479947153867624624 / OpenLayers.METERS_PER_INCH,
    "GoldCoastFoot": 0.30479971018150881758 / OpenLayers.METERS_PER_INCH,
    "IInch": 0.02540000000000000000 / OpenLayers.METERS_PER_INCH,
    "MicroInch": 0.00002540000000000000 / OpenLayers.METERS_PER_INCH,
    "Mil": 0.00000002540000000000 / OpenLayers.METERS_PER_INCH,
    "Centimeter": 0.01000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Kilometer": 1000.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Yard": 0.91440182880365760731 / OpenLayers.METERS_PER_INCH,
    "SearsYard": 0.914398414616029 / OpenLayers.METERS_PER_INCH,
    "IndianYard": 0.91439853074444079983 / OpenLayers.METERS_PER_INCH,
    "IndianYd37": 0.91439523 / OpenLayers.METERS_PER_INCH,
    "IndianYd62": 0.9143988 / OpenLayers.METERS_PER_INCH,
    "IndianYd75": 0.9143985 / OpenLayers.METERS_PER_INCH,
    "IndianFoot": 0.30479951 / OpenLayers.METERS_PER_INCH,
    "IndianFt37": 0.30479841 / OpenLayers.METERS_PER_INCH,
    "IndianFt62": 0.3047996 / OpenLayers.METERS_PER_INCH,
    "IndianFt75": 0.3047995 / OpenLayers.METERS_PER_INCH,
    "Mile": 1609.34721869443738887477 / OpenLayers.METERS_PER_INCH,
    "IYard": 0.91440000000000000000 / OpenLayers.METERS_PER_INCH,
    "IMile": 1609.34400000000000000000 / OpenLayers.METERS_PER_INCH,
    "NautM": 1852.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Lat-66": 110943.316488932731 / OpenLayers.METERS_PER_INCH,
    "Lat-83": 110946.25736872234125 / OpenLayers.METERS_PER_INCH,
    "Decimeter": 0.10000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Millimeter": 0.00100000000000000000 / OpenLayers.METERS_PER_INCH,
    "Dekameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Decameter": 10.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "Hectometer": 100.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "GermanMeter": 1.0000135965 / OpenLayers.METERS_PER_INCH,
    "CaGrid": 0.999738 / OpenLayers.METERS_PER_INCH,
    "ClarkeChain": 20.1166194976 / OpenLayers.METERS_PER_INCH,
    "GunterChain": 20.11684023368047 / OpenLayers.METERS_PER_INCH,
    "BenoitChain": 20.116782494375872 / OpenLayers.METERS_PER_INCH,
    "SearsChain": 20.11676512155 / OpenLayers.METERS_PER_INCH,
    "ClarkeLink": 0.201166194976 / OpenLayers.METERS_PER_INCH,
    "GunterLink": 0.2011684023368047 / OpenLayers.METERS_PER_INCH,
    "BenoitLink": 0.20116782494375872 / OpenLayers.METERS_PER_INCH,
    "SearsLink": 0.2011676512155 / OpenLayers.METERS_PER_INCH,
    "Rod": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
    "IntnlChain": 20.1168 / OpenLayers.METERS_PER_INCH,
    "IntnlLink": 0.201168 / OpenLayers.METERS_PER_INCH,
    "Perch": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
    "Pole": 5.02921005842012 / OpenLayers.METERS_PER_INCH,
    "Furlong": 201.1684023368046 / OpenLayers.METERS_PER_INCH,
    "Rood": 3.778266898 / OpenLayers.METERS_PER_INCH,
    "CapeFoot": 0.3047972615 / OpenLayers.METERS_PER_INCH,
    "Brealey": 375.00000000000000000000 / OpenLayers.METERS_PER_INCH,
    "ModAmFt": 0.304812252984505969011938 / OpenLayers.METERS_PER_INCH,
    "Fathom": 1.8288 / OpenLayers.METERS_PER_INCH,
    "NautM-UK": 1853.184 / OpenLayers.METERS_PER_INCH,
    "50kilometers": 50000.0 / OpenLayers.METERS_PER_INCH,
    "150kilometers": 150000.0 / OpenLayers.METERS_PER_INCH
});
OpenLayers.Util.extend(OpenLayers.INCHES_PER_UNIT, {
    "mm": OpenLayers.INCHES_PER_UNIT["Meter"] / 1000.0,
    "cm": OpenLayers.INCHES_PER_UNIT["Meter"] / 100.0,
    "dm": OpenLayers.INCHES_PER_UNIT["Meter"] * 100.0,
    "km": OpenLayers.INCHES_PER_UNIT["Meter"] * 1000.0,
    "kmi": OpenLayers.INCHES_PER_UNIT["nmi"],
    "fath": OpenLayers.INCHES_PER_UNIT["Fathom"],
    "ch": OpenLayers.INCHES_PER_UNIT["IntnlChain"],
    "link": OpenLayers.INCHES_PER_UNIT["IntnlLink"],
    "us-in": OpenLayers.INCHES_PER_UNIT["inches"],
    "us-ft": OpenLayers.INCHES_PER_UNIT["Foot"],
    "us-yd": OpenLayers.INCHES_PER_UNIT["Yard"],
    "us-ch": OpenLayers.INCHES_PER_UNIT["GunterChain"],
    "us-mi": OpenLayers.INCHES_PER_UNIT["Mile"],
    "ind-yd": OpenLayers.INCHES_PER_UNIT["IndianYd37"],
    "ind-ft": OpenLayers.INCHES_PER_UNIT["IndianFt37"],
    "ind-ch": 20.11669506 / OpenLayers.METERS_PER_INCH
});
OpenLayers.DOTS_PER_INCH = 72;
OpenLayers.Util.normalizeScale = function (scale) {
    var normScale = (scale > 1.0) ? (1.0 / scale) : scale;
    return normScale;
};
OpenLayers.Util.getResolutionFromScale = function (scale, units) {
    var resolution;
    if (scale) {
        if (units == null) {
            units = "degrees";
        }
        var normScale = OpenLayers.Util.normalizeScale(scale);
        resolution = 1 / (normScale * OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH);
    }
    return resolution;
};
OpenLayers.Util.getScaleFromResolution = function (resolution, units) {
    if (units == null) {
        units = "degrees";
    }
    var scale = resolution * OpenLayers.INCHES_PER_UNIT[units] * OpenLayers.DOTS_PER_INCH;
    return scale;
};
OpenLayers.Util.safeStopPropagation = function (evt) {
    OpenLayers.Event.stop(evt, true);
};
OpenLayers.Util.pagePosition = function (forElement) {
    var pos = [0, 0];
    var viewportElement = OpenLayers.Util.getViewportElement();
    if (!forElement || forElement == window || forElement == viewportElement) {
        return pos;
    }
    var BUGGY_GECKO_BOX_OBJECT = OpenLayers.IS_GECKO && document.getBoxObjectFor && OpenLayers.Element.getStyle(forElement, 'position') == 'absolute' && (forElement.style.top == '' || forElement.style.left == '');
    var parent = null;
    var box;
    if (forElement.getBoundingClientRect) {
        box = forElement.getBoundingClientRect();
        var scrollTop = viewportElement.scrollTop;
        var scrollLeft = viewportElement.scrollLeft;
        pos[0] = box.left + scrollLeft;
        pos[1] = box.top + scrollTop;
    } else if (document.getBoxObjectFor && !BUGGY_GECKO_BOX_OBJECT) {
        box = document.getBoxObjectFor(forElement);
        var vpBox = document.getBoxObjectFor(viewportElement);
        pos[0] = box.screenX - vpBox.screenX;
        pos[1] = box.screenY - vpBox.screenY;
    } else {
        pos[0] = forElement.offsetLeft;
        pos[1] = forElement.offsetTop;
        parent = forElement.offsetParent;
        if (parent != forElement) {
            while (parent) {
                pos[0] += parent.offsetLeft;
                pos[1] += parent.offsetTop;
                parent = parent.offsetParent;
            }
        }
        var browser = OpenLayers.BROWSER_NAME;
        if (browser == "opera" || (browser == "safari" && OpenLayers.Element.getStyle(forElement, 'position') == 'absolute')) {
            pos[1] -= document.body.offsetTop;
        }
        parent = forElement.offsetParent;
        while (parent && parent != document.body) {
            pos[0] -= parent.scrollLeft;
            if (browser != "opera" || parent.tagName != 'TR') {
                pos[1] -= parent.scrollTop;
            }
            parent = parent.offsetParent;
        }
    }
    return pos;
};
OpenLayers.Util.getViewportElement = function () {
    var viewportElement = arguments.callee.viewportElement;
    if (viewportElement == undefined) {
        viewportElement = (OpenLayers.BROWSER_NAME == "msie" && document.compatMode != 'CSS1Compat') ? document.body : document.documentElement;
        arguments.callee.viewportElement = viewportElement;
    }
    return viewportElement;
};
OpenLayers.Util.isEquivalentUrl = function (url1, url2, options) {
    options = options || {};
    OpenLayers.Util.applyDefaults(options, {
        ignoreCase: true,
        ignorePort80: true,
        ignoreHash: true
    });
    var urlObj1 = OpenLayers.Util.createUrlObject(url1, options);
    var urlObj2 = OpenLayers.Util.createUrlObject(url2, options);
    for (var key in urlObj1) {
        if (key !== "args") {
            if (urlObj1[key] != urlObj2[key]) {
                return false;
            }
        }
    }
    for (var key in urlObj1.args) {
        if (urlObj1.args[key] != urlObj2.args[key]) {
            return false;
        }
        delete urlObj2.args[key];
    }
    for (var key in urlObj2.args) {
        return false;
    }
    return true;
};
OpenLayers.Util.createUrlObject = function (url, options) {
    options = options || {};
    if (!(/^\w+:\/\//).test(url)) {
        var loc = window.location;
        var port = loc.port ? ":" + loc.port : "";
        var fullUrl = loc.protocol + "//" + loc.host.split(":").shift() + port;
        if (url.indexOf("/") === 0) {
            url = fullUrl + url;
        } else {
            var parts = loc.pathname.split("/");
            parts.pop();
            url = fullUrl + parts.join("/") + "/" + url;
        }
    }
    if (options.ignoreCase) {
        url = url.toLowerCase();
    }
    var a = document.createElement('a');
    a.href = url;
    var urlObject = {};
    urlObject.host = a.host.split(":").shift();
    urlObject.protocol = a.protocol;
    if (options.ignorePort80) {
        urlObject.port = (a.port == "80" || a.port == "0") ? "" : a.port;
    } else {
        urlObject.port = (a.port == "" || a.port == "0") ? "80" : a.port;
    }
    urlObject.hash = (options.ignoreHash || a.hash === "#") ? "" : a.hash;
    var queryString = a.search;
    if (!queryString) {
        var qMark = url.indexOf("?");
        queryString = (qMark != -1) ? url.substr(qMark) : "";
    }
    urlObject.args = OpenLayers.Util.getParameters(queryString);
    urlObject.pathname = (a.pathname.charAt(0) == "/") ? a.pathname : "/" + a.pathname;
    return urlObject;
};
OpenLayers.Util.removeTail = function (url) {
    var head = null;
    var qMark = url.indexOf("?");
    var hashMark = url.indexOf("#");
    if (qMark == -1) {
        head = (hashMark != -1) ? url.substr(0, hashMark) : url;
    } else {
        head = (hashMark != -1) ? url.substr(0, Math.min(qMark, hashMark)) : url.substr(0, qMark);
    }
    return head;
};
OpenLayers.IS_GECKO = (function () {
    var ua = navigator.userAgent.toLowerCase();
    return ua.indexOf("webkit") == -1 && ua.indexOf("gecko") != -1;
})();
OpenLayers.BROWSER_NAME = (function () {
    var name = "";
    var ua = navigator.userAgent.toLowerCase();
    if (ua.indexOf("opera") != -1) {
        name = "opera";
    } else if (ua.indexOf("msie") != -1) {
        name = "msie";
    } else if (ua.indexOf("safari") != -1) {
        name = "safari";
    } else if (ua.indexOf("mozilla") != -1) {
        if (ua.indexOf("firefox") != -1) {
            name = "firefox";
        } else {
            name = "mozilla";
        }
    }
    return name;
})();
OpenLayers.Util.getBrowserName = function () {
    return OpenLayers.BROWSER_NAME;
};
OpenLayers.Util.getRenderedDimensions = function (contentHTML, size, options) {
    var w, h;
    var container = document.createElement("div");
    container.style.visibility = "hidden";
    var containerElement = (options && options.containerElement) ? options.containerElement : document.body;
    if (size) {
        if (size.w) {
            w = size.w;
            container.style.width = w + "px";
        } else if (size.h) {
            h = size.h;
            container.style.height = h + "px";
        }
    }
    if (options && options.displayClass) {
        container.className = options.displayClass;
    }
    var content = document.createElement("div");
    content.innerHTML = contentHTML;
    content.style.overflow = "visible";
    if (content.childNodes) {
        for (var i = 0, l = content.childNodes.length; i < l; i++) {
            if (!content.childNodes[i].style) continue;
            content.childNodes[i].style.overflow = "visible";
        }
    }
    container.appendChild(content);
    containerElement.appendChild(container);
    var parentHasPositionAbsolute = false;
    var parent = container.parentNode;
    while (parent && parent.tagName.toLowerCase() != "body") {
        var parentPosition = OpenLayers.Element.getStyle(parent, "position");
        if (parentPosition == "absolute") {
            parentHasPositionAbsolute = true;
            break;
        } else if (parentPosition && parentPosition != "static") {
            break;
        }
        parent = parent.parentNode;
    }
    if (!parentHasPositionAbsolute) {
        container.style.position = "absolute";
    }
    if (!w) {
        w = parseInt(content.scrollWidth);
        container.style.width = w + "px";
    }
    if (!h) {
        h = parseInt(content.scrollHeight);
    }
    container.removeChild(content);
    containerElement.removeChild(container);
    return new OpenLayers.Size(w, h);
};
OpenLayers.Util.getScrollbarWidth = function () {
    var scrollbarWidth = OpenLayers.Util._scrollbarWidth;
    if (scrollbarWidth == null) {
        var scr = null;
        var inn = null;
        var wNoScroll = 0;
        var wScroll = 0;
        scr = document.createElement('div');
        scr.style.position = 'absolute';
        scr.style.top = '-1000px';
        scr.style.left = '-1000px';
        scr.style.width = '100px';
        scr.style.height = '50px';
        scr.style.overflow = 'hidden';
        inn = document.createElement('div');
        inn.style.width = '100%';
        inn.style.height = '200px';
        scr.appendChild(inn);
        document.body.appendChild(scr);
        wNoScroll = inn.offsetWidth;
        scr.style.overflow = 'scroll';
        wScroll = inn.offsetWidth;
        document.body.removeChild(document.body.lastChild);
        OpenLayers.Util._scrollbarWidth = (wNoScroll - wScroll);
        scrollbarWidth = OpenLayers.Util._scrollbarWidth;
    }
    return scrollbarWidth;
};
OpenLayers.Util.getFormattedLonLat = function (coordinate, axis, dmsOption) {
    if (!dmsOption) {
        dmsOption = 'dms';
    }
    coordinate = (coordinate + 540) % 360 - 180;
    var abscoordinate = Math.abs(coordinate);
    var coordinatedegrees = Math.floor(abscoordinate);
    var coordinateminutes = (abscoordinate - coordinatedegrees) / (1 / 60);
    var tempcoordinateminutes = coordinateminutes;
    coordinateminutes = Math.floor(coordinateminutes);
    var coordinateseconds = (tempcoordinateminutes - coordinateminutes) / (1 / 60);
    coordinateseconds = Math.round(coordinateseconds * 10);
    coordinateseconds /= 10;
    if (coordinateseconds >= 60) {
        coordinateseconds -= 60;
        coordinateminutes += 1;
        if (coordinateminutes >= 60) {
            coordinateminutes -= 60;
            coordinatedegrees += 1;
        }
    }
    if (coordinatedegrees < 10) {
        coordinatedegrees = "0" + coordinatedegrees;
    }
    var str = coordinatedegrees + "\u00B0";
    if (dmsOption.indexOf('dm') >= 0) {
        if (coordinateminutes < 10) {
            coordinateminutes = "0" + coordinateminutes;
        }
        str += coordinateminutes + "'";
        if (dmsOption.indexOf('dms') >= 0) {
            if (coordinateseconds < 10) {
                coordinateseconds = "0" + coordinateseconds;
            }
            str += coordinateseconds + '"';
        }
    }
    if (axis == "lon") {
        str += coordinate < 0 ? OpenLayers.i18n("W") : OpenLayers.i18n("E");
    } else {
        str += coordinate < 0 ? OpenLayers.i18n("S") : OpenLayers.i18n("N");
    }
    return str;
};
OpenLayers.Format = OpenLayers.Class({
    options: null,
    externalProjection: null,
    internalProjection: null,
    data: null,
    keepData: false,
    initialize: function (options) {
        OpenLayers.Util.extend(this, options);
        this.options = options;
    },
    destroy: function () {},
    read: function (data) {
        OpenLayers.Console.userError(OpenLayers.i18n("readNotImplemented"));
    },
    write: function (object) {
        OpenLayers.Console.userError(OpenLayers.i18n("writeNotImplemented"));
    },
    CLASS_NAME: "OpenLayers.Format"
});
OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, {
    indent: "    ",
    space: " ",
    newline: "\n",
    level: 0,
    pretty: false,
    nativeJSON: (function () {
        return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function");
    })(),
    read: function (json, filter) {
        var object;
        if (this.nativeJSON) {
            object = JSON.parse(json, filter);
        } else try {
            if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
                object = eval('(' + json + ')');
                if (typeof filter === 'function') {
                    function walk(k, v) {
                        if (v && typeof v === 'object') {
                            for (var i in v) {
                                if (v.hasOwnProperty(i)) {
                                    v[i] = walk(i, v[i]);
                                }
                            }
                        }
                        return filter(k, v);
                    }
                    object = walk('', object);
                }
            }
        } catch (e) {}
        if (this.keepData) {
            this.data = object;
        }
        return object;
    },
    write: function (value, pretty) {
        this.pretty = !! pretty;
        var json = null;
        var type = typeof value;
        if (this.serialize[type]) {
            try {
                json = (!this.pretty && this.nativeJSON) ? JSON.stringify(value) : this.serialize[type].apply(this, [value]);
            } catch (err) {
                OpenLayers.Console.error("Trouble serializing: " + err);
            }
        }
        return json;
    },
    writeIndent: function () {
        var pieces = [];
        if (this.pretty) {
            for (var i = 0; i < this.level; ++i) {
                pieces.push(this.indent);
            }
        }
        return pieces.join('');
    },
    writeNewline: function () {
        return (this.pretty) ? this.newline : '';
    },
    writeSpace: function () {
        return (this.pretty) ? this.space : '';
    },
    serialize: {
        'object': function (object) {
            if (object == null) {
                return "null";
            }
            if (object.constructor == Date) {
                return this.serialize.date.apply(this, [object]);
            }
            if (object.constructor == Array) {
                return this.serialize.array.apply(this, [object]);
            }
            var pieces = ['{'];
            this.level += 1;
            var key, keyJSON, valueJSON;
            var addComma = false;
            for (key in object) {
                if (object.hasOwnProperty(key)) {
                    keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [key, this.pretty]);
                    valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, [object[key], this.pretty]);
                    if (keyJSON != null && valueJSON != null) {
                        if (addComma) {
                            pieces.push(',');
                        }
                        pieces.push(this.writeNewline(), this.writeIndent(), keyJSON, ':', this.writeSpace(), valueJSON);
                        addComma = true;
                    }
                }
            }
            this.level -= 1;
            pieces.push(this.writeNewline(), this.writeIndent(), '}');
            return pieces.join('');
        },
        'array': function (array) {
            var json;
            var pieces = ['['];
            this.level += 1;
            for (var i = 0, len = array.length; i < len; ++i) {
                json = OpenLayers.Format.JSON.prototype.write.apply(this, [array[i], this.pretty]);
                if (json != null) {
                    if (i > 0) {
                        pieces.push(',');
                    }
                    pieces.push(this.writeNewline(), this.writeIndent(), json);
                }
            }
            this.level -= 1;
            pieces.push(this.writeNewline(), this.writeIndent(), ']');
            return pieces.join('');
        },
        'string': function (string) {
            var m = {
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"': '\\"',
                '\\': '\\\\'
            };
            if (/["\\\x00-\x1f]/.test(string)) {
                return '"' + string.replace(/([\x00-\x1f\\"])/g, function (a, b) {
                    var c = m[b];
                    if (c) {
                        return c;
                    }
                    c = b.charCodeAt();
                    return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
                }) + '"';
            }
            return '"' + string + '"';
        },
        'number': function (number) {
            return isFinite(number) ? String(number) : "null";
        },
        'boolean': function (bool) {
            return String(bool);
        },
        'date': function (date) {
            function format(number) {
                return (number < 10) ? '0' + number : number;
            }
            return '"' + date.getFullYear() + '-' + format(date.getMonth() + 1) + '-' + format(date.getDate()) + 'T' + format(date.getHours()) + ':' + format(date.getMinutes()) + ':' + format(date.getSeconds()) + '"';
        }
    },
    CLASS_NAME: "OpenLayers.Format.JSON"
});
OpenLayers.Feature = OpenLayers.Class({
    layer: null,
    id: null,
    lonlat: null,
    data: null,
    marker: null,
    popupClass: null,
    popup: null,
    initialize: function (layer, lonlat, data) {
        this.layer = layer;
        this.lonlat = lonlat;
        this.data = (data != null) ? data : {};
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    destroy: function () {
        if ((this.layer != null) && (this.layer.map != null)) {
            if (this.popup != null) {
                this.layer.map.removePopup(this.popup);
            }
        }
        if (this.layer != null && this.marker != null) {
            this.layer.removeMarker(this.marker);
        }
        this.layer = null;
        this.id = null;
        this.lonlat = null;
        this.data = null;
        if (this.marker != null) {
            this.destroyMarker(this.marker);
            this.marker = null;
        }
        if (this.popup != null) {
            this.destroyPopup(this.popup);
            this.popup = null;
        }
    },
    onScreen: function () {
        var onScreen = false;
        if ((this.layer != null) && (this.layer.map != null)) {
            var screenBounds = this.layer.map.getExtent();
            onScreen = screenBounds.containsLonLat(this.lonlat);
        }
        return onScreen;
    },
    createMarker: function () {
        if (this.lonlat != null) {
            this.marker = new OpenLayers.Marker(this.lonlat, this.data.icon);
        }
        return this.marker;
    },
    destroyMarker: function () {
        this.marker.destroy();
    },
    createPopup: function (closeBox) {
        if (this.lonlat != null) {
            if (!this.popup) {
                var anchor = (this.marker) ? this.marker.icon : null;
                var popupClass = this.popupClass ? this.popupClass : OpenLayers.Popup.AnchoredBubble;
                this.popup = new popupClass(this.id + "_popup", this.lonlat, this.data.popupSize, this.data.popupContentHTML, anchor, closeBox);
            }
            if (this.data.overflow != null) {
                this.popup.contentDiv.style.overflow = this.data.overflow;
            }
            this.popup.feature = this;
        }
        return this.popup;
    },
    destroyPopup: function () {
        if (this.popup) {
            this.popup.feature = null;
            this.popup.destroy();
            this.popup = null;
        }
    },
    CLASS_NAME: "OpenLayers.Feature"
});
OpenLayers.State = {
    UNKNOWN: 'Unknown',
    INSERT: 'Insert',
    UPDATE: 'Update',
    DELETE: 'Delete'
};
OpenLayers.Feature.Vector = OpenLayers.Class(OpenLayers.Feature, {
    fid: null,
    geometry: null,
    attributes: null,
    bounds: null,
    state: null,
    style: null,
    url: null,
    renderIntent: "default",
    modified: null,
    initialize: function (geometry, attributes, style) {
        OpenLayers.Feature.prototype.initialize.apply(this, [null, null, attributes]);
        this.lonlat = null;
        this.geometry = geometry ? geometry : null;
        this.state = null;
        this.attributes = {};
        if (attributes) {
            this.attributes = OpenLayers.Util.extend(this.attributes, attributes);
        }
        this.style = style ? style : null;
    },
    destroy: function () {
        if (this.layer) {
            this.layer.removeFeatures(this);
            this.layer = null;
        }
        this.geometry = null;
        this.modified = null;
        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
    },
    clone: function () {
        return new OpenLayers.Feature.Vector(this.geometry ? this.geometry.clone() : null, this.attributes, this.style);
    },
    onScreen: function (boundsOnly) {
        var onScreen = false;
        if (this.layer && this.layer.map) {
            var screenBounds = this.layer.map.getExtent();
            if (boundsOnly) {
                var featureBounds = this.geometry.getBounds();
                onScreen = screenBounds.intersectsBounds(featureBounds);
            } else {
                var screenPoly = screenBounds.toGeometry();
                onScreen = screenPoly.intersects(this.geometry);
            }
        }
        return onScreen;
    },
    getVisibility: function () {
        return !(this.style && this.style.display == 'none' || !this.layer || this.layer && this.layer.styleMap && this.layer.styleMap.createSymbolizer(this, this.renderIntent).display == 'none' || this.layer && !this.layer.getVisibility());
    },
    createMarker: function () {
        return null;
    },
    destroyMarker: function () {},
    createPopup: function () {
        return null;
    },
    atPoint: function (lonlat, toleranceLon, toleranceLat) {
        var atPoint = false;
        if (this.geometry) {
            atPoint = this.geometry.atPoint(lonlat, toleranceLon, toleranceLat);
        }
        return atPoint;
    },
    destroyPopup: function () {},
    move: function (location) {
        if (!this.layer || !this.geometry.move) {
            return undefined;
        }
        var pixel;
        if (location.CLASS_NAME == "OpenLayers.LonLat") {
            pixel = this.layer.getViewPortPxFromLonLat(location);
        } else {
            pixel = location;
        }
        var lastPixel = this.layer.getViewPortPxFromLonLat(this.geometry.getBounds().getCenterLonLat());
        var res = this.layer.map.getResolution();
        this.geometry.move(res * (pixel.x - lastPixel.x), res * (lastPixel.y - pixel.y));
        this.layer.drawFeature(this);
        return lastPixel;
    },
    toState: function (state) {
        if (state == OpenLayers.State.UPDATE) {
            switch (this.state) {
            case OpenLayers.State.UNKNOWN:
            case OpenLayers.State.DELETE:
                this.state = state;
                break;
            case OpenLayers.State.UPDATE:
            case OpenLayers.State.INSERT:
                break;
            }
        } else if (state == OpenLayers.State.INSERT) {
            switch (this.state) {
            case OpenLayers.State.UNKNOWN:
                break;
            default:
                this.state = state;
                break;
            }
        } else if (state == OpenLayers.State.DELETE) {
            switch (this.state) {
            case OpenLayers.State.INSERT:
                break;
            case OpenLayers.State.DELETE:
                break;
            case OpenLayers.State.UNKNOWN:
            case OpenLayers.State.UPDATE:
                this.state = state;
                break;
            }
        } else if (state == OpenLayers.State.UNKNOWN) {
            this.state = state;
        }
    },
    CLASS_NAME: "OpenLayers.Feature.Vector"
});
OpenLayers.Feature.Vector.style = {
    'default': {
        fillColor: "#ee9900",
        fillOpacity: 0.4,
        hoverFillColor: "white",
        hoverFillOpacity: 0.8,
        strokeColor: "#ee9900",
        strokeOpacity: 1,
        strokeWidth: 1,
        strokeLinecap: "round",
        strokeDashstyle: "solid",
        hoverStrokeColor: "red",
        hoverStrokeOpacity: 1,
        hoverStrokeWidth: 0.2,
        pointRadius: 6,
        hoverPointRadius: 1,
        hoverPointUnit: "%",
        pointerEvents: "visiblePainted",
        cursor: "inherit"
    },
    'select': {
        fillColor: "blue",
        fillOpacity: 0.4,
        hoverFillColor: "white",
        hoverFillOpacity: 0.8,
        strokeColor: "blue",
        strokeOpacity: 1,
        strokeWidth: 2,
        strokeLinecap: "round",
        strokeDashstyle: "solid",
        hoverStrokeColor: "red",
        hoverStrokeOpacity: 1,
        hoverStrokeWidth: 0.2,
        pointRadius: 6,
        hoverPointRadius: 1,
        hoverPointUnit: "%",
        pointerEvents: "visiblePainted",
        cursor: "pointer"
    },
    'temporary': {
        fillColor: "#66cccc",
        fillOpacity: 0.2,
        hoverFillColor: "white",
        hoverFillOpacity: 0.8,
        strokeColor: "#66cccc",
        strokeOpacity: 1,
        strokeLinecap: "round",
        strokeWidth: 2,
        strokeDashstyle: "solid",
        hoverStrokeColor: "red",
        hoverStrokeOpacity: 1,
        hoverStrokeWidth: 0.2,
        pointRadius: 6,
        hoverPointRadius: 1,
        hoverPointUnit: "%",
        pointerEvents: "visiblePainted",
        cursor: "inherit"
    },
    'delete': {
        display: "none"
    }
};
OpenLayers.Format.WKT = OpenLayers.Class(OpenLayers.Format, {
    initialize: function (options) {
        this.regExes = {
            'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
            'spaces': /\s+/,
            'parenComma': /\)\s*,\s*\(/,
            'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,
            'trimParens': /^\s*\(?(.*?)\)?\s*$/
        };
        OpenLayers.Format.prototype.initialize.apply(this, [options]);
    },
    read: function (wkt) {
        var features, type, str;
        wkt = wkt.replace(/[\n\r]/g, " ");
        var matches = this.regExes.typeStr.exec(wkt);
        if (matches) {
            type = matches[1].toLowerCase();
            str = matches[2];
            if (this.parse[type]) {
                features = this.parse[type].apply(this, [str]);
            }
            if (this.internalProjection && this.externalProjection) {
                if (features && features.CLASS_NAME == "OpenLayers.Feature.Vector") {
                    features.geometry.transform(this.externalProjection, this.internalProjection);
                } else if (features && type != "geometrycollection" && typeof features == "object") {
                    for (var i = 0, len = features.length; i < len; i++) {
                        var component = features[i];
                        component.geometry.transform(this.externalProjection, this.internalProjection);
                    }
                }
            }
        }
        return features;
    },
    write: function (features) {
        var collection, geometry, type, data, isCollection;
        if (features.constructor == Array) {
            collection = features;
            isCollection = true;
        } else {
            collection = [features];
            isCollection = false;
        }
        var pieces = [];
        if (isCollection) {
            pieces.push('GEOMETRYCOLLECTION(');
        }
        for (var i = 0, len = collection.length; i < len; ++i) {
            if (isCollection && i > 0) {
                pieces.push(',');
            }
            geometry = collection[i].geometry;
            pieces.push(this.extractGeometry(geometry));
        }
        if (isCollection) {
            pieces.push(')');
        }
        return pieces.join('');
    },
    extractGeometry: function (geometry) {
        var type = geometry.CLASS_NAME.split('.')[2].toLowerCase();
        if (!this.extract[type]) {
            return null;
        }
        if (this.internalProjection && this.externalProjection) {
            geometry = geometry.clone();
            geometry.transform(this.internalProjection, this.externalProjection);
        }
        var wktType = type == 'collection' ? 'GEOMETRYCOLLECTION' : type.toUpperCase();
        var data = wktType + '(' + this.extract[type].apply(this, [geometry]) + ')';
        return data;
    },
    extract: {
        'point': function (point) {
            return point.x + ' ' + point.y;
        },
        'multipoint': function (multipoint) {
            var array = [];
            for (var i = 0, len = multipoint.components.length; i < len; ++i) {
                array.push('(' + this.extract.point.apply(this, [multipoint.components[i]]) + ')');
            }
            return array.join(',');
        },
        'linestring': function (linestring) {
            var array = [];
            for (var i = 0, len = linestring.components.length; i < len; ++i) {
                array.push(this.extract.point.apply(this, [linestring.components[i]]));
            }
            return array.join(',');
        },
        'multilinestring': function (multilinestring) {
            var array = [];
            for (var i = 0, len = multilinestring.components.length; i < len; ++i) {
                array.push('(' + this.extract.linestring.apply(this, [multilinestring.components[i]]) + ')');
            }
            return array.join(',');
        },
        'polygon': function (polygon) {
            var array = [];
            for (var i = 0, len = polygon.components.length; i < len; ++i) {
                array.push('(' + this.extract.linestring.apply(this, [polygon.components[i]]) + ')');
            }
            return array.join(',');
        },
        'multipolygon': function (multipolygon) {
            var array = [];
            for (var i = 0, len = multipolygon.components.length; i < len; ++i) {
                array.push('(' + this.extract.polygon.apply(this, [multipolygon.components[i]]) + ')');
            }
            return array.join(',');
        },
        'collection': function (collection) {
            var array = [];
            for (var i = 0, len = collection.components.length; i < len; ++i) {
                array.push(this.extractGeometry.apply(this, [collection.components[i]]));
            }
            return array.join(',');
        }
    },
    parse: {
        'point': function (str) {
            var coords = OpenLayers.String.trim(str).split(this.regExes.spaces);
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(coords[0], coords[1]));
        },
        'multipoint': function (str) {
            var point;
            var points = OpenLayers.String.trim(str).split(',');
            var components = [];
            for (var i = 0, len = points.length; i < len; ++i) {
                point = points[i].replace(this.regExes.trimParens, '$1');
                components.push(this.parse.point.apply(this, [point]).geometry);
            }
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint(components));
        },
        'linestring': function (str) {
            var points = OpenLayers.String.trim(str).split(',');
            var components = [];
            for (var i = 0, len = points.length; i < len; ++i) {
                components.push(this.parse.point.apply(this, [points[i]]).geometry);
            }
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(components));
        },
        'multilinestring': function (str) {
            var line;
            var lines = OpenLayers.String.trim(str).split(this.regExes.parenComma);
            var components = [];
            for (var i = 0, len = lines.length; i < len; ++i) {
                line = lines[i].replace(this.regExes.trimParens, '$1');
                components.push(this.parse.linestring.apply(this, [line]).geometry);
            }
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiLineString(components));
        },
        'polygon': function (str) {
            var ring, linestring, linearring;
            var rings = OpenLayers.String.trim(str).split(this.regExes.parenComma);
            var components = [];
            for (var i = 0, len = rings.length; i < len; ++i) {
                ring = rings[i].replace(this.regExes.trimParens, '$1');
                linestring = this.parse.linestring.apply(this, [ring]).geometry;
                linearring = new OpenLayers.Geometry.LinearRing(linestring.components);
                components.push(linearring);
            }
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon(components));
        },
        'multipolygon': function (str) {
            var polygon;
            var polygons = OpenLayers.String.trim(str).split(this.regExes.doubleParenComma);
            var components = [];
            for (var i = 0, len = polygons.length; i < len; ++i) {
                polygon = polygons[i].replace(this.regExes.trimParens, '$1');
                components.push(this.parse.polygon.apply(this, [polygon]).geometry);
            }
            return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPolygon(components));
        },
        'geometrycollection': function (str) {
            str = str.replace(/,\s*([A-Za-z])/g, '|$1');
            var wktArray = OpenLayers.String.trim(str).split('|');
            var components = [];
            for (var i = 0, len = wktArray.length; i < len; ++i) {
                components.push(OpenLayers.Format.WKT.prototype.read.apply(this, [wktArray[i]]));
            }
            return components;
        }
    },
    CLASS_NAME: "OpenLayers.Format.WKT"
});
OpenLayers.Protocol.SQL.Gears = OpenLayers.Class(OpenLayers.Protocol.SQL, {
    FID_PREFIX: '__gears_fid__',
    NULL_GEOMETRY: '__gears_null_geometry__',
    NULL_FEATURE_STATE: '__gears_null_feature_state__',
    jsonParser: null,
    wktParser: null,
    fidRegExp: null,
    saveFeatureState: true,
    typeOfFid: "string",
    db: null,
    initialize: function (options) {
        if (!this.supported()) {
            return;
        }
        OpenLayers.Protocol.SQL.prototype.initialize.apply(this, [options]);
        this.jsonParser = new OpenLayers.Format.JSON();
        this.wktParser = new OpenLayers.Format.WKT();
        this.fidRegExp = new RegExp('^' + this.FID_PREFIX);
        this.initializeDatabase();
    },
    initializeDatabase: function () {
        this.db = google.gears.factory.create('beta.database');
        this.db.open(this.databaseName);
        this.db.execute("CREATE TABLE IF NOT EXISTS " + this.tableName + " (fid TEXT UNIQUE, geometry TEXT, properties TEXT," + "  state TEXT)");
    },
    destroy: function () {
        this.db.close();
        this.db = null;
        this.jsonParser = null;
        this.wktParser = null;
        OpenLayers.Protocol.SQL.prototype.destroy.apply(this);
    },
    supported: function () {
        return !!(window.google && google.gears);
    },
    read: function (options) {
        OpenLayers.Protocol.prototype.read.apply(this, arguments);
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var feature, features = [];
        var rs = this.db.execute("SELECT * FROM " + this.tableName);
        while (rs.isValidRow()) {
            feature = this.unfreezeFeature(rs);
            if (this.evaluateFilter(feature, options.filter)) {
                if (!options.noFeatureStateReset) {
                    feature.state = null;
                }
                features.push(feature);
            }
            rs.next();
        }
        rs.close();
        var resp = new OpenLayers.Protocol.Response({
            code: OpenLayers.Protocol.Response.SUCCESS,
            requestType: "read",
            features: features
        });
        if (options && options.callback) {
            options.callback.call(options.scope, resp);
        }
        return resp;
    },
    unfreezeFeature: function (row) {
        var feature;
        var wkt = row.fieldByName('geometry');
        if (wkt == this.NULL_GEOMETRY) {
            feature = new OpenLayers.Feature.Vector();
        } else {
            feature = this.wktParser.read(wkt);
        }
        feature.attributes = this.jsonParser.read(row.fieldByName('properties'));
        feature.fid = this.extractFidFromField(row.fieldByName('fid'));
        var state = row.fieldByName('state');
        if (state == this.NULL_FEATURE_STATE) {
            state = null;
        }
        feature.state = state;
        return feature;
    },
    extractFidFromField: function (field) {
        if (!field.match(this.fidRegExp) && this.typeOfFid == "number") {
            field = parseFloat(field);
        }
        return field;
    },
    create: function (features, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = this.createOrUpdate(features);
        resp.requestType = "create";
        if (options && options.callback) {
            options.callback.call(options.scope, resp);
        }
        return resp;
    },
    update: function (features, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = this.createOrUpdate(features);
        resp.requestType = "update";
        if (options && options.callback) {
            options.callback.call(options.scope, resp);
        }
        return resp;
    },
    createOrUpdate: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        var i, len = features.length,
            feature;
        var insertedFeatures = new Array(len);
        for (i = 0; i < len; i++) {
            feature = features[i];
            var params = this.freezeFeature(feature);
            this.db.execute("REPLACE INTO " + this.tableName + " (fid, geometry, properties, state)" + " VALUES (?, ?, ?, ?)", params);
            var clone = feature.clone();
            clone.fid = this.extractFidFromField(params[0]);
            insertedFeatures[i] = clone;
        }
        return new OpenLayers.Protocol.Response({
            code: OpenLayers.Protocol.Response.SUCCESS,
            features: insertedFeatures,
            reqFeatures: features
        });
    },
    freezeFeature: function (feature) {
        feature.fid = feature.fid != null ? "" + feature.fid : OpenLayers.Util.createUniqueID(this.FID_PREFIX);
        var geometry = feature.geometry != null ? feature.geometry.toString() : this.NULL_GEOMETRY;
        var properties = this.jsonParser.write(feature.attributes);
        var state = this.getFeatureStateForFreeze(feature);
        return [feature.fid, geometry, properties, state];
    },
    getFeatureStateForFreeze: function (feature) {
        var state;
        if (!this.saveFeatureState) {
            state = this.NULL_FEATURE_STATE;
        } else if (this.createdOffline(feature)) {
            state = OpenLayers.State.INSERT;
        } else {
            state = feature.state;
        }
        return state;
    },
    "delete": function (features, options) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var i, len, feature;
        for (i = 0, len = features.length; i < len; i++) {
            feature = features[i];
            if (this.saveFeatureState && !this.createdOffline(feature)) {
                var toDelete = feature.clone();
                toDelete.fid = feature.fid;
                if (toDelete.geometry) {
                    toDelete.geometry.destroy();
                    toDelete.geometry = null;
                }
                toDelete.state = feature.state;
                this.createOrUpdate(toDelete);
            } else {
                this.db.execute("DELETE FROM " + this.tableName + " WHERE fid = ?", [feature.fid]);
            }
        }
        var resp = new OpenLayers.Protocol.Response({
            code: OpenLayers.Protocol.Response.SUCCESS,
            requestType: "delete",
            reqFeatures: features
        });
        if (options && options.callback) {
            options.callback.call(options.scope, resp);
        }
        return resp;
    },
    createdOffline: function (feature) {
        return (typeof feature.fid == "string" && !! (feature.fid.match(this.fidRegExp)));
    },
    commit: function (features, options) {
        var opt, resp = [],
            nRequests = 0,
            nResponses = 0;

        function callback(resp) {
            if (++nResponses < nRequests) {
                resp.last = false;
            }
            this.callUserCallback(options, resp);
        }
        var feature, toCreate = [],
            toUpdate = [],
            toDelete = [];
        for (var i = features.length - 1; i >= 0; i--) {
            feature = features[i];
            switch (feature.state) {
            case OpenLayers.State.INSERT:
                toCreate.push(feature);
                break;
            case OpenLayers.State.UPDATE:
                toUpdate.push(feature);
                break;
            case OpenLayers.State.DELETE:
                toDelete.push(feature);
                break;
            }
        }
        if (toCreate.length > 0) {
            nRequests++;
            opt = OpenLayers.Util.applyDefaults({
                "callback": callback,
                "scope": this
            }, options.create);
            resp.push(this.create(toCreate, opt));
        }
        if (toUpdate.length > 0) {
            nRequests++;
            opt = OpenLayers.Util.applyDefaults({
                "callback": callback,
                "scope": this
            }, options.update);
            resp.push(this.update(toUpdate, opt));
        }
        if (toDelete.length > 0) {
            nRequests++;
            opt = OpenLayers.Util.applyDefaults({
                "callback": callback,
                "scope": this
            }, options["delete"]);
            resp.push(this["delete"](toDelete, opt));
        }
        return resp;
    },
    clear: function () {
        this.db.execute("DELETE FROM " + this.tableName);
    },
    callUserCallback: function (options, resp) {
        var opt = options[resp.requestType];
        if (opt && opt.callback) {
            opt.callback.call(opt.scope, resp);
        }
        if (resp.last && options.callback) {
            options.callback.call(options.scope);
        }
    },
    CLASS_NAME: "OpenLayers.Protocol.SQL.Gears"
});
OpenLayers.Event = {
    observers: false,
    KEY_BACKSPACE: 8,
    KEY_TAB: 9,
    KEY_RETURN: 13,
    KEY_ESC: 27,
    KEY_LEFT: 37,
    KEY_UP: 38,
    KEY_RIGHT: 39,
    KEY_DOWN: 40,
    KEY_DELETE: 46,
    element: function (event) {
        return event.target || event.srcElement;
    },
    isSingleTouch: function (event) {
        return event.touches && event.touches.length == 1;
    },
    isMultiTouch: function (event) {
        return event.touches && event.touches.length > 1;
    },
    isLeftClick: function (event) {
        return (((event.which) && (event.which == 1)) || ((event.button) && (event.button == 1)));
    },
    isRightClick: function (event) {
        return (((event.which) && (event.which == 3)) || ((event.button) && (event.button == 2)));
    },
    stop: function (event, allowDefault) {
        if (!allowDefault) {
            if (event.preventDefault) {
                event.preventDefault();
            } else {
                event.returnValue = false;
            }
        }
        if (event.stopPropagation) {
            event.stopPropagation();
        } else {
            event.cancelBubble = true;
        }
    },
    findElement: function (event, tagName) {
        var element = OpenLayers.Event.element(event);
        while (element.parentNode && (!element.tagName || (element.tagName.toUpperCase() != tagName.toUpperCase()))) {
            element = element.parentNode;
        }
        return element;
    },
    observe: function (elementParam, name, observer, useCapture) {
        var element = OpenLayers.Util.getElement(elementParam);
        useCapture = useCapture || false;
        if (name == 'keypress' && (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) {
            name = 'keydown';
        }
        if (!this.observers) {
            this.observers = {};
        }
        if (!element._eventCacheID) {
            var idPrefix = "eventCacheID_";
            if (element.id) {
                idPrefix = element.id + "_" + idPrefix;
            }
            element._eventCacheID = OpenLayers.Util.createUniqueID(idPrefix);
        }
        var cacheID = element._eventCacheID;
        if (!this.observers[cacheID]) {
            this.observers[cacheID] = [];
        }
        this.observers[cacheID].push({
            'element': element,
            'name': name,
            'observer': observer,
            'useCapture': useCapture
        });
        if (element.addEventListener) {
            element.addEventListener(name, observer, useCapture);
        } else if (element.attachEvent) {
            element.attachEvent('on' + name, observer);
        }
    },
    stopObservingElement: function (elementParam) {
        var element = OpenLayers.Util.getElement(elementParam);
        var cacheID = element._eventCacheID;
        this._removeElementObservers(OpenLayers.Event.observers[cacheID]);
    },
    _removeElementObservers: function (elementObservers) {
        if (elementObservers) {
            for (var i = elementObservers.length - 1; i >= 0; i--) {
                var entry = elementObservers[i];
                var args = new Array(entry.element, entry.name, entry.observer, entry.useCapture);
                var removed = OpenLayers.Event.stopObserving.apply(this, args);
            }
        }
    },
    stopObserving: function (elementParam, name, observer, useCapture) {
        useCapture = useCapture || false;
        var element = OpenLayers.Util.getElement(elementParam);
        var cacheID = element._eventCacheID;
        if (name == 'keypress') {
            if (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent) {
                name = 'keydown';
            }
        }
        var foundEntry = false;
        var elementObservers = OpenLayers.Event.observers[cacheID];
        if (elementObservers) {
            var i = 0;
            while (!foundEntry && i < elementObservers.length) {
                var cacheEntry = elementObservers[i];
                if ((cacheEntry.name == name) && (cacheEntry.observer == observer) && (cacheEntry.useCapture == useCapture)) {
                    elementObservers.splice(i, 1);
                    if (elementObservers.length == 0) {
                        delete OpenLayers.Event.observers[cacheID];
                    }
                    foundEntry = true;
                    break;
                }
                i++;
            }
        }
        if (foundEntry) {
            if (element.removeEventListener) {
                element.removeEventListener(name, observer, useCapture);
            } else if (element && element.detachEvent) {
                element.detachEvent('on' + name, observer);
            }
        }
        return foundEntry;
    },
    unloadCache: function () {
        if (OpenLayers.Event && OpenLayers.Event.observers) {
            for (var cacheID in OpenLayers.Event.observers) {
                var elementObservers = OpenLayers.Event.observers[cacheID];
                OpenLayers.Event._removeElementObservers.apply(this, [elementObservers]);
            }
            OpenLayers.Event.observers = false;
        }
    },
    CLASS_NAME: "OpenLayers.Event"
};
OpenLayers.Event.observe(window, 'unload', OpenLayers.Event.unloadCache, false);
if (window.Event) {
    OpenLayers.Util.applyDefaults(window.Event, OpenLayers.Event);
} else {
    var Event = OpenLayers.Event;
}
OpenLayers.Events = OpenLayers.Class({
    BROWSER_EVENTS: ["mouseover", "mouseout", "mousedown", "mouseup", "mousemove", "click", "dblclick", "rightclick", "dblrightclick", "resize", "focus", "blur", "touchstart", "touchmove", "touchend"],
    listeners: null,
    object: null,
    element: null,
    eventTypes: null,
    eventHandler: null,
    fallThrough: null,
    includeXY: false,
    clearMouseListener: null,
    initialize: function (object, element, eventTypes, fallThrough, options) {
        OpenLayers.Util.extend(this, options);
        this.object = object;
        this.fallThrough = fallThrough;
        this.listeners = {};
        this.eventHandler = OpenLayers.Function.bindAsEventListener(this.handleBrowserEvent, this);
        this.clearMouseListener = OpenLayers.Function.bind(this.clearMouseCache, this);
        this.eventTypes = [];
        if (eventTypes != null) {
            for (var i = 0, len = eventTypes.length; i < len; i++) {
                this.addEventType(eventTypes[i]);
            }
        }
        if (element != null) {
            this.attachToElement(element);
        }
    },
    destroy: function () {
        if (this.element) {
            OpenLayers.Event.stopObservingElement(this.element);
            if (this.element.hasScrollEvent) {
                OpenLayers.Event.stopObserving(window, "scroll", this.clearMouseListener);
            }
        }
        this.element = null;
        this.listeners = null;
        this.object = null;
        this.eventTypes = null;
        this.fallThrough = null;
        this.eventHandler = null;
    },
    addEventType: function (eventName) {
        if (!this.listeners[eventName]) {
            this.eventTypes.push(eventName);
            this.listeners[eventName] = [];
        }
    },
    attachToElement: function (element) {
        if (this.element) {
            OpenLayers.Event.stopObservingElement(this.element);
        }
        this.element = element;
        for (var i = 0, len = this.BROWSER_EVENTS.length; i < len; i++) {
            var eventType = this.BROWSER_EVENTS[i];
            this.addEventType(eventType);
            OpenLayers.Event.observe(element, eventType, this.eventHandler);
        }
        OpenLayers.Event.observe(element, "dragstart", OpenLayers.Event.stop);
    },
    on: function (object) {
        for (var type in object) {
            if (type != "scope") {
                this.register(type, object.scope, object[type]);
            }
        }
    },
    register: function (type, obj, func) {
        if ((func != null) && (OpenLayers.Util.indexOf(this.eventTypes, type) != -1)) {
            if (obj == null) {
                obj = this.object;
            }
            var listeners = this.listeners[type];
            listeners.push({
                obj: obj,
                func: func
            });
        }
    },
    registerPriority: function (type, obj, func) {
        if (func != null) {
            if (obj == null) {
                obj = this.object;
            }
            var listeners = this.listeners[type];
            if (listeners != null) {
                listeners.unshift({
                    obj: obj,
                    func: func
                });
            }
        }
    },
    un: function (object) {
        for (var type in object) {
            if (type != "scope") {
                this.unregister(type, object.scope, object[type]);
            }
        }
    },
    unregister: function (type, obj, func) {
        if (obj == null) {
            obj = this.object;
        }
        var listeners = this.listeners[type];
        if (listeners != null) {
            for (var i = 0, len = listeners.length; i < len; i++) {
                if (listeners[i].obj == obj && listeners[i].func == func) {
                    listeners.splice(i, 1);
                    break;
                }
            }
        }
    },
    remove: function (type) {
        if (this.listeners[type] != null) {
            this.listeners[type] = [];
        }
    },
    triggerEvent: function (type, evt) {
        var listeners = this.listeners[type];
        if (!listeners || listeners.length == 0) {
            return undefined;
        }
        if (evt == null) {
            evt = {};
        }
        evt.object = this.object;
        evt.element = this.element;
        if (!evt.type) {
            evt.type = type;
        }
        listeners = listeners.slice();
        var continueChain;
        for (var i = 0, len = listeners.length; i < len; i++) {
            var callback = listeners[i];
            continueChain = callback.func.apply(callback.obj, [evt]);
            if ((continueChain != undefined) && (continueChain == false)) {
                break;
            }
        }
        if (!this.fallThrough) {
            OpenLayers.Event.stop(evt, true);
        }
        return continueChain;
    },
    handleBrowserEvent: function (evt) {
        var type = evt.type,
            listeners = this.listeners[type];
        if (!listeners || listeners.length == 0) {
            return;
        }
        var touches = evt.touches;
        if (touches && touches[0]) {
            var x = 0;
            var y = 0;
            var num = touches.length;
            var touch;
            for (var i = 0; i < num; ++i) {
                touch = touches[i];
                x += touch.clientX;
                y += touch.clientY;
            }
            evt.clientX = x / num;
            evt.clientY = y / num;
        }
        if (this.includeXY) {
            evt.xy = this.getMousePosition(evt);
        }
        this.triggerEvent(type, evt);
    },
    clearMouseCache: function () {
        this.element.scrolls = null;
        this.element.lefttop = null;
        var body = document.body;
        if (body && !((body.scrollTop != 0 || body.scrollLeft != 0) && navigator.userAgent.match(/iPhone/i))) {
            this.element.offsets = null;
        }
    },
    getMousePosition: function (evt) {
        if (!this.includeXY) {
            this.clearMouseCache();
        } else if (!this.element.hasScrollEvent) {
            OpenLayers.Event.observe(window, "scroll", this.clearMouseListener);
            this.element.hasScrollEvent = true;
        }
        if (!this.element.scrolls) {
            var viewportElement = OpenLayers.Util.getViewportElement();
            this.element.scrolls = [viewportElement.scrollLeft, viewportElement.scrollTop];
        }
        if (!this.element.lefttop) {
            this.element.lefttop = [(document.documentElement.clientLeft || 0), (document.documentElement.clientTop || 0)];
        }
        if (!this.element.offsets) {
            this.element.offsets = OpenLayers.Util.pagePosition(this.element);
        }
        return new OpenLayers.Pixel((evt.clientX + this.element.scrolls[0]) - this.element.offsets[0] - this.element.lefttop[0], (evt.clientY + this.element.scrolls[1]) - this.element.offsets[1] - this.element.lefttop[1]);
    },
    CLASS_NAME: "OpenLayers.Events"
});
OpenLayers.Tween = OpenLayers.Class({
    INTERVAL: 10,
    easing: null,
    begin: null,
    finish: null,
    duration: null,
    callbacks: null,
    time: null,
    interval: null,
    playing: false,
    initialize: function (easing) {
        this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut;
    },
    start: function (begin, finish, duration, options) {
        this.playing = true;
        this.begin = begin;
        this.finish = finish;
        this.duration = duration;
        this.callbacks = options.callbacks;
        this.time = 0;
        if (this.interval) {
            window.clearInterval(this.interval);
            this.interval = null;
        }
        if (this.callbacks && this.callbacks.start) {
            this.callbacks.start.call(this, this.begin);
        }
        this.interval = window.setInterval(OpenLayers.Function.bind(this.play, this), this.INTERVAL);
    },
    stop: function () {
        if (!this.playing) {
            return;
        }
        if (this.callbacks && this.callbacks.done) {
            this.callbacks.done.call(this, this.finish);
        }
        window.clearInterval(this.interval);
        this.interval = null;
        this.playing = false;
    },
    play: function () {
        var value = {};
        for (var i in this.begin) {
            var b = this.begin[i];
            var f = this.finish[i];
            if (b == null || f == null || isNaN(b) || isNaN(f)) {
                OpenLayers.Console.error('invalid value for Tween');
            }
            var c = f - b;
            value[i] = this.easing.apply(this, [this.time, b, c, this.duration]);
        }
        this.time++;
        if (this.callbacks && this.callbacks.eachStep) {
            this.callbacks.eachStep.call(this, value);
        }
        if (this.time > this.duration) {
            this.stop();
        }
    },
    CLASS_NAME: "OpenLayers.Tween"
});
OpenLayers.Easing = {
    CLASS_NAME: "OpenLayers.Easing"
};
OpenLayers.Easing.Linear = {
    easeIn: function (t, b, c, d) {
        return c * t / d + b;
    },
    easeOut: function (t, b, c, d) {
        return c * t / d + b;
    },
    easeInOut: function (t, b, c, d) {
        return c * t / d + b;
    },
    CLASS_NAME: "OpenLayers.Easing.Linear"
};
OpenLayers.Easing.Expo = {
    easeIn: function (t, b, c, d) {
        return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
    },
    easeOut: function (t, b, c, d) {
        return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
    },
    easeInOut: function (t, b, c, d) {
        if (t == 0) return b;
        if (t == d) return b + c;
        if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
        return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
    },
    CLASS_NAME: "OpenLayers.Easing.Expo"
};
OpenLayers.Easing.Quad = {
    easeIn: function (t, b, c, d) {
        return c * (t /= d) * t + b;
    },
    easeOut: function (t, b, c, d) {
        return -c * (t /= d) * (t - 2) + b;
    },
    easeInOut: function (t, b, c, d) {
        if ((t /= d / 2) < 1) return c / 2 * t * t + b;
        return -c / 2 * ((--t) * (t - 2) - 1) + b;
    },
    CLASS_NAME: "OpenLayers.Easing.Quad"
};
OpenLayers.Map = OpenLayers.Class({
    Z_INDEX_BASE: {
        BaseLayer: 100,
        Overlay: 325,
        Feature: 725,
        Popup: 750,
        Control: 1000
    },
    EVENT_TYPES: ["preaddlayer", "addlayer", "preremovelayer", "removelayer", "changelayer", "movestart", "move", "moveend", "zoomend", "popupopen", "popupclose", "addmarker", "removemarker", "clearmarkers", "mouseover", "mouseout", "mousemove", "dragstart", "drag", "dragend", "changebaselayer"],
    id: null,
    fractionalZoom: false,
    events: null,
    allOverlays: false,
    div: null,
    dragging: false,
    size: null,
    viewPortDiv: null,
    layerContainerOrigin: null,
    layerContainerDiv: null,
    layers: null,
    controls: null,
    popups: null,
    baseLayer: null,
    center: null,
    resolution: null,
    zoom: 0,
    panRatio: 1.5,
    viewRequestID: 0,
    tileSize: null,
    projection: "EPSG:4326",
    units: 'degrees',
    resolutions: null,
    maxResolution: 1.40625,
    minResolution: null,
    maxScale: null,
    minScale: null,
    maxExtent: null,
    minExtent: null,
    restrictedExtent: null,
    numZoomLevels: 16,
    theme: null,
    displayProjection: null,
    fallThrough: true,
    panTween: null,
    eventListeners: null,
    panMethod: OpenLayers.Easing.Expo.easeOut,
    panDuration: 50,
    paddingForPopups: null,
    minPx: null,
    maxPx: null,
    initialize: function (div, options) {
        if (arguments.length === 1 && typeof div === "object") {
            options = div;
            div = options && options.div;
        }
        this.tileSize = new OpenLayers.Size(OpenLayers.Map.TILE_WIDTH, OpenLayers.Map.TILE_HEIGHT);
        this.maxExtent = new OpenLayers.Bounds(-180, -90, 180, 90);
        this.paddingForPopups = new OpenLayers.Bounds(15, 15, 15, 15);
        this.theme = OpenLayers._getScriptLocation() + 'theme/default/style.css';
        OpenLayers.Util.extend(this, options);
        this.layers = [];
        this.id = OpenLayers.Util.createUniqueID("OpenLayers.Map_");
        this.div = OpenLayers.Util.getElement(div);
        if (!this.div) {
            this.div = document.createElement("div");
            this.div.style.height = "1px";
            this.div.style.width = "1px";
        }
        OpenLayers.Element.addClass(this.div, 'olMap');
        var id = this.id + "_OpenLayers_ViewPort";
        this.viewPortDiv = OpenLayers.Util.createDiv(id, null, null, null, "relative", null, "hidden");
        this.viewPortDiv.style.width = "100%";
        this.viewPortDiv.style.height = "100%";
        this.viewPortDiv.className = "olMapViewport";
        this.div.appendChild(this.viewPortDiv);
        var eventsDiv = document.createElement("div");
        eventsDiv.id = this.id + "_events";
        eventsDiv.style.position = "absolute";
        eventsDiv.style.width = "100%";
        eventsDiv.style.height = "100%";
        eventsDiv.style.zIndex = this.Z_INDEX_BASE.Control - 1;
        this.viewPortDiv.appendChild(eventsDiv);
        this.eventsDiv = eventsDiv;
        this.events = new OpenLayers.Events(this, this.eventsDiv, this.EVENT_TYPES, this.fallThrough, {
            includeXY: true
        });
        id = this.id + "_OpenLayers_Container";
        this.layerContainerDiv = OpenLayers.Util.createDiv(id);
        this.layerContainerDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] - 1;
        this.eventsDiv.appendChild(this.layerContainerDiv);
        this.updateSize();
        if (this.eventListeners instanceof Object) {
            this.events.on(this.eventListeners);
        }
        this.events.register("movestart", this, this.updateSize);
        if (OpenLayers.String.contains(navigator.appName, "Microsoft")) {
            this.events.register("resize", this, this.updateSize);
        } else {
            this.updateSizeDestroy = OpenLayers.Function.bind(this.updateSize, this);
            OpenLayers.Event.observe(window, 'resize', this.updateSizeDestroy);
        }
        if (this.theme) {
            var addNode = true;
            var nodes = document.getElementsByTagName('link');
            for (var i = 0, len = nodes.length; i < len; ++i) {
                if (OpenLayers.Util.isEquivalentUrl(nodes.item(i).href, this.theme)) {
                    addNode = false;
                    break;
                }
            }
            if (addNode) {
                var cssNode = document.createElement('link');
                cssNode.setAttribute('rel', 'stylesheet');
                cssNode.setAttribute('type', 'text/css');
                cssNode.setAttribute('href', this.theme);
                document.getElementsByTagName('head')[0].appendChild(cssNode);
            }
        }
        if (this.controls == null) {
            if (OpenLayers.Control != null) {
                this.controls = [new OpenLayers.Control.Navigation(), new OpenLayers.Control.PanZoom(), new OpenLayers.Control.ArgParser(), new OpenLayers.Control.Attribution()];
            } else {
                this.controls = [];
            }
        }
        for (var i = 0, len = this.controls.length; i < len; i++) {
            this.addControlToMap(this.controls[i]);
        }
        this.popups = [];
        this.unloadDestroy = OpenLayers.Function.bind(this.destroy, this);
        OpenLayers.Event.observe(window, 'unload', this.unloadDestroy);
        if (options && options.layers) {
            delete this.center;
            this.addLayers(options.layers);
            if (options.center) {
                this.setCenter(options.center, options.zoom);
            }
        }
    },
    render: function (div) {
        this.div = OpenLayers.Util.getElement(div);
        OpenLayers.Element.addClass(this.div, 'olMap');
        this.viewPortDiv.parentNode.removeChild(this.viewPortDiv);
        this.div.appendChild(this.viewPortDiv);
        this.updateSize();
    },
    unloadDestroy: null,
    updateSizeDestroy: null,
    destroy: function () {
        if (!this.unloadDestroy) {
            return false;
        }
        if (this.panTween) {
            this.panTween.stop();
            this.panTween = null;
        }
        OpenLayers.Event.stopObserving(window, 'unload', this.unloadDestroy);
        this.unloadDestroy = null;
        if (this.updateSizeDestroy) {
            OpenLayers.Event.stopObserving(window, 'resize', this.updateSizeDestroy);
        } else {
            this.events.unregister("resize", this, this.updateSize);
        }
        this.paddingForPopups = null;
        if (this.controls != null) {
            for (var i = this.controls.length - 1; i >= 0; --i) {
                this.controls[i].destroy();
            }
            this.controls = null;
        }
        if (this.layers != null) {
            for (var i = this.layers.length - 1; i >= 0; --i) {
                this.layers[i].destroy(false);
            }
            this.layers = null;
        }
        if (this.viewPortDiv) {
            this.div.removeChild(this.viewPortDiv);
        }
        this.viewPortDiv = null;
        if (this.eventListeners) {
            this.events.un(this.eventListeners);
            this.eventListeners = null;
        }
        this.events.destroy();
        this.events = null;
    },
    setOptions: function (options) {
        var updatePxExtent = this.minPx && options.restrictedExtent != this.restrictedExtent;
        OpenLayers.Util.extend(this, options);
        updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
            forceZoomChange: true
        });
    },
    getTileSize: function () {
        return this.tileSize;
    },
    getBy: function (array, property, match) {
        var test = (typeof match.test == "function");
        var found = OpenLayers.Array.filter(this[array], function (item) {
            return item[property] == match || (test && match.test(item[property]));
        });
        return found;
    },
    getLayersBy: function (property, match) {
        return this.getBy("layers", property, match);
    },
    getLayersByName: function (match) {
        return this.getLayersBy("name", match);
    },
    getLayersByClass: function (match) {
        return this.getLayersBy("CLASS_NAME", match);
    },
    getControlsBy: function (property, match) {
        return this.getBy("controls", property, match);
    },
    getControlsByClass: function (match) {
        return this.getControlsBy("CLASS_NAME", match);
    },
    getLayer: function (id) {
        var foundLayer = null;
        for (var i = 0, len = this.layers.length; i < len; i++) {
            var layer = this.layers[i];
            if (layer.id == id) {
                foundLayer = layer;
                break;
            }
        }
        return foundLayer;
    },
    setLayerZIndex: function (layer, zIdx) {
        layer.setZIndex(this.Z_INDEX_BASE[layer.isBaseLayer ? 'BaseLayer' : 'Overlay'] + zIdx * 5);
    },
    resetLayersZIndex: function () {
        for (var i = 0, len = this.layers.length; i < len; i++) {
            var layer = this.layers[i];
            this.setLayerZIndex(layer, i);
        }
    },
    addLayer: function (layer) {
        for (var i = 0, len = this.layers.length; i < len; i++) {
            if (this.layers[i] == layer) {
                var msg = OpenLayers.i18n('layerAlreadyAdded', {
                    'layerName': layer.name
                });
                OpenLayers.Console.warn(msg);
                return false;
            }
        }
        if (this.events.triggerEvent("preaddlayer", {
            layer: layer
        }) === false) {
            return;
        }
        if (this.allOverlays) {
            layer.isBaseLayer = false;
        }
        layer.div.className = "olLayerDiv";
        layer.div.style.overflow = "";
        this.setLayerZIndex(layer, this.layers.length);
        if (layer.isFixed) {
            this.viewPortDiv.appendChild(layer.div);
        } else {
            this.layerContainerDiv.appendChild(layer.div);
        }
        this.layers.push(layer);
        layer.setMap(this);
        if (layer.isBaseLayer || (this.allOverlays && !this.baseLayer)) {
            if (this.baseLayer == null) {
                this.setBaseLayer(layer);
            } else {
                layer.setVisibility(false);
            }
        } else {
            layer.redraw();
        }
        this.events.triggerEvent("addlayer", {
            layer: layer
        });
        layer.events.triggerEvent("added", {
            map: this,
            layer: layer
        });
        layer.afterAdd();
    },
    addLayers: function (layers) {
        for (var i = 0, len = layers.length; i < len; i++) {
            this.addLayer(layers[i]);
        }
    },
    removeLayer: function (layer, setNewBaseLayer) {
        if (this.events.triggerEvent("preremovelayer", {
            layer: layer
        }) === false) {
            return;
        }
        if (setNewBaseLayer == null) {
            setNewBaseLayer = true;
        }
        if (layer.isFixed) {
            this.viewPortDiv.removeChild(layer.div);
        } else {
            this.layerContainerDiv.removeChild(layer.div);
        }
        OpenLayers.Util.removeItem(this.layers, layer);
        layer.removeMap(this);
        layer.map = null;
        if (this.baseLayer == layer) {
            this.baseLayer = null;
            if (setNewBaseLayer) {
                for (var i = 0, len = this.layers.length; i < len; i++) {
                    var iLayer = this.layers[i];
                    if (iLayer.isBaseLayer || this.allOverlays) {
                        this.setBaseLayer(iLayer);
                        break;
                    }
                }
            }
        }
        this.resetLayersZIndex();
        this.events.triggerEvent("removelayer", {
            layer: layer
        });
        layer.events.triggerEvent("removed", {
            map: this,
            layer: layer
        });
    },
    getNumLayers: function () {
        return this.layers.length;
    },
    getLayerIndex: function (layer) {
        return OpenLayers.Util.indexOf(this.layers, layer);
    },
    setLayerIndex: function (layer, idx) {
        var base = this.getLayerIndex(layer);
        if (idx < 0) {
            idx = 0;
        } else if (idx > this.layers.length) {
            idx = this.layers.length;
        }
        if (base != idx) {
            this.layers.splice(base, 1);
            this.layers.splice(idx, 0, layer);
            for (var i = 0, len = this.layers.length; i < len; i++) {
                this.setLayerZIndex(this.layers[i], i);
            }
            this.events.triggerEvent("changelayer", {
                layer: layer,
                property: "order"
            });
            if (this.allOverlays) {
                if (idx === 0) {
                    this.setBaseLayer(layer);
                } else if (this.baseLayer !== this.layers[0]) {
                    this.setBaseLayer(this.layers[0]);
                }
            }
        }
    },
    raiseLayer: function (layer, delta) {
        var idx = this.getLayerIndex(layer) + delta;
        this.setLayerIndex(layer, idx);
    },
    setBaseLayer: function (newBaseLayer) {
        if (newBaseLayer != this.baseLayer) {
            if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) {
                var center = this.getCachedCenter();
                var newResolution = OpenLayers.Util.getResolutionFromScale(this.getScale(), newBaseLayer.units);
                if (this.baseLayer != null && !this.allOverlays) {
                    this.baseLayer.setVisibility(false);
                }
                this.baseLayer = newBaseLayer;
                this.viewRequestID++;
                if (!this.allOverlays || this.baseLayer.visibility) {
                    this.baseLayer.setVisibility(true);
                }
                if (center != null) {
                    var newZoom = this.getZoomForResolution(newResolution || this.resolution, true);
                    this.setCenter(center, newZoom, false, true);
                }
                this.events.triggerEvent("changebaselayer", {
                    layer: this.baseLayer
                });
            }
        }
    },
    addControl: function (control, px) {
        this.controls.push(control);
        this.addControlToMap(control, px);
    },
    addControls: function (controls, pixels) {
        var pxs = (arguments.length === 1) ? [] : pixels;
        for (var i = 0, len = controls.length; i < len; i++) {
            var ctrl = controls[i];
            var px = (pxs[i]) ? pxs[i] : null;
            this.addControl(ctrl, px);
        }
    },
    addControlToMap: function (control, px) {
        control.outsideViewport = (control.div != null);
        if (this.displayProjection && !control.displayProjection) {
            control.displayProjection = this.displayProjection;
        }
        control.setMap(this);
        var div = control.draw(px);
        if (div) {
            if (!control.outsideViewport) {
                div.style.zIndex = this.Z_INDEX_BASE['Control'] + this.controls.length;
                this.viewPortDiv.appendChild(div);
            }
        }
        if (control.autoActivate) {
            control.activate();
        }
    },
    getControl: function (id) {
        var returnControl = null;
        for (var i = 0, len = this.controls.length; i < len; i++) {
            var control = this.controls[i];
            if (control.id == id) {
                returnControl = control;
                break;
            }
        }
        return returnControl;
    },
    removeControl: function (control) {
        if ((control) && (control == this.getControl(control.id))) {
            if (control.div && (control.div.parentNode == this.viewPortDiv)) {
                this.viewPortDiv.removeChild(control.div);
            }
            OpenLayers.Util.removeItem(this.controls, control);
        }
    },
    addPopup: function (popup, exclusive) {
        if (exclusive) {
            for (var i = this.popups.length - 1; i >= 0; --i) {
                this.removePopup(this.popups[i]);
            }
        }
        popup.map = this;
        this.popups.push(popup);
        var popupDiv = popup.draw();
        if (popupDiv) {
            popupDiv.style.zIndex = this.Z_INDEX_BASE['Popup'] + this.popups.length;
            this.layerContainerDiv.appendChild(popupDiv);
        }
    },
    removePopup: function (popup) {
        OpenLayers.Util.removeItem(this.popups, popup);
        if (popup.div) {
            try {
                this.layerContainerDiv.removeChild(popup.div);
            } catch (e) {}
        }
        popup.map = null;
    },
    getSize: function () {
        var size = null;
        if (this.size != null) {
            size = this.size.clone();
        }
        return size;
    },
    updateSize: function () {
        var newSize = this.getCurrentSize();
        if (newSize && !isNaN(newSize.h) && !isNaN(newSize.w)) {
            this.events.clearMouseCache();
            var oldSize = this.getSize();
            if (oldSize == null) {
                this.size = oldSize = newSize;
            }
            if (!newSize.equals(oldSize)) {
                this.size = newSize;
                for (var i = 0, len = this.layers.length; i < len; i++) {
                    this.layers[i].onMapResize();
                }
                var center = this.getCachedCenter();
                if (this.baseLayer != null && center != null) {
                    var zoom = this.getZoom();
                    this.zoom = null;
                    this.setCenter(center, zoom);
                }
            }
        }
    },
    getCurrentSize: function () {
        var size = new OpenLayers.Size(this.div.clientWidth, this.div.clientHeight);
        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
            size.w = this.div.offsetWidth;
            size.h = this.div.offsetHeight;
        }
        if (size.w == 0 && size.h == 0 || isNaN(size.w) && isNaN(size.h)) {
            size.w = parseInt(this.div.style.width);
            size.h = parseInt(this.div.style.height);
        }
        return size;
    },
    calculateBounds: function (center, resolution) {
        var extent = null;
        if (center == null) {
            center = this.getCachedCenter();
        }
        if (resolution == null) {
            resolution = this.getResolution();
        }
        if ((center != null) && (resolution != null)) {
            var size = this.getSize();
            var w_deg = size.w * resolution;
            var h_deg = size.h * resolution;
            extent = new OpenLayers.Bounds(center.lon - w_deg / 2, center.lat - h_deg / 2, center.lon + w_deg / 2, center.lat + h_deg / 2);
        }
        return extent;
    },
    getCenter: function () {
        var center = null;
        var cachedCenter = this.getCachedCenter();
        if (cachedCenter) {
            center = cachedCenter.clone();
        }
        return center;
    },
    getCachedCenter: function () {
        if (!this.center && this.size) {
            this.center = this.getLonLatFromViewPortPx(new OpenLayers.Pixel(this.size.w / 2, this.size.h / 2));
        }
        return this.center;
    },
    getZoom: function () {
        return this.zoom;
    },
    pan: function (dx, dy, options) {
        options = OpenLayers.Util.applyDefaults(options, {
            animate: true,
            dragging: false
        });
        if (options.dragging) {
            if (dx != 0 || dy != 0) {
                this.moveByPx(dx, dy);
            }
        } else {
            var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
            var newCenterPx = centerPx.add(dx, dy);
            if (this.dragging || !newCenterPx.equals(centerPx)) {
                var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
                if (options.animate) {
                    this.panTo(newCenterLonLat);
                } else {
                    this.moveTo(newCenterLonLat);
                    this.dragging = false;
                    this.events.triggerEvent("moveend");
                }
            }
        }
    },
    panTo: function (lonlat) {
        if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
            if (!this.panTween) {
                this.panTween = new OpenLayers.Tween(this.panMethod);
            }
            var center = this.getCachedCenter();
            if (lonlat.equals(center)) {
                return;
            }
            var from = this.getPixelFromLonLat(center);
            var to = this.getPixelFromLonLat(lonlat);
            var vector = {
                x: to.x - from.x,
                y: to.y - from.y
            };
            var last = {
                x: 0,
                y: 0
            };
            this.panTween.start({
                x: 0,
                y: 0
            }, vector, this.panDuration, {
                callbacks: {
                    eachStep: OpenLayers.Function.bind(function (px) {
                        var x = px.x - last.x,
                            y = px.y - last.y;
                        this.moveByPx(x, y);
                        last.x = Math.round(px.x);
                        last.y = Math.round(px.y);
                    }, this),
                    done: OpenLayers.Function.bind(function (px) {
                        this.moveTo(lonlat);
                        this.dragging = false;
                        this.events.triggerEvent("moveend");
                    }, this)
                }
            });
        } else {
            this.setCenter(lonlat);
        }
    },
    setCenter: function (lonlat, zoom, dragging, forceZoomChange) {
        this.panTween && this.panTween.stop();
        this.moveTo(lonlat, zoom, {
            'dragging': dragging,
            'forceZoomChange': forceZoomChange
        });
    },
    moveByPx: function (dx, dy) {
        var hw = this.size.w / 2;
        var hh = this.size.h / 2;
        var x = hw + dx;
        var y = hh + dy;
        var wrapDateLine = this.baseLayer.wrapDateLine;
        var xRestriction = 0;
        var yRestriction = 0;
        if (this.restrictedExtent) {
            xRestriction = hw;
            yRestriction = hh;
            wrapDateLine = false;
        }
        dx = wrapDateLine || x <= this.maxPx.x - xRestriction && x >= this.minPx.x + xRestriction ? Math.round(dx) : 0;
        dy = y <= this.maxPx.y - yRestriction && y >= this.minPx.y + yRestriction ? Math.round(dy) : 0;
        var minX = this.minPx.x,
            maxX = this.maxPx.x;
        if (dx || dy) {
            if (!this.dragging) {
                this.dragging = true;
                this.events.triggerEvent("movestart");
            }
            this.center = null;
            if (dx) {
                this.layerContainerDiv.style.left = parseInt(this.layerContainerDiv.style.left) - dx + "px";
                this.minPx.x -= dx;
                this.maxPx.x -= dx;
                if (wrapDateLine) {
                    if (this.maxPx.x > maxX) {
                        this.maxPx.x -= (maxX - minX);
                    }
                    if (this.minPx.x < minX) {
                        this.minPx.x += (maxX - minX);
                    }
                }
            }
            if (dy) {
                this.layerContainerDiv.style.top = parseInt(this.layerContainerDiv.style.top) - dy + "px";
                this.minPx.y -= dy;
                this.maxPx.y -= dy;
            }
            var layer, i, len;
            for (i = 0, len = this.layers.length; i < len; ++i) {
                layer = this.layers[i];
                if (layer.visibility && (layer === this.baseLayer || layer.inRange)) {
                    layer.moveByPx(dx, dy);
                    layer.events.triggerEvent("move");
                }
            }
            this.events.triggerEvent("move");
        }
    },
    moveTo: function (lonlat, zoom, options) {
        if (!options) {
            options = {};
        }
        if (zoom != null) {
            zoom = parseFloat(zoom);
            if (!this.fractionalZoom) {
                zoom = Math.round(zoom);
            }
        }
        var dragging = options.dragging || this.dragging;
        var forceZoomChange = options.forceZoomChange;
        if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
            lonlat = this.maxExtent.getCenterLonLat();
            this.center = lonlat.clone();
        }
        if (this.restrictedExtent != null) {
            if (lonlat == null) {
                lonlat = this.center;
            }
            if (zoom == null) {
                zoom = this.getZoom();
            }
            var resolution = this.getResolutionForZoom(zoom);
            var extent = this.calculateBounds(lonlat, resolution);
            if (!this.restrictedExtent.containsBounds(extent)) {
                var maxCenter = this.restrictedExtent.getCenterLonLat();
                if (extent.getWidth() > this.restrictedExtent.getWidth()) {
                    lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
                } else if (extent.left < this.restrictedExtent.left) {
                    lonlat = lonlat.add(this.restrictedExtent.left - extent.left, 0);
                } else if (extent.right > this.restrictedExtent.right) {
                    lonlat = lonlat.add(this.restrictedExtent.right - extent.right, 0);
                }
                if (extent.getHeight() > this.restrictedExtent.getHeight()) {
                    lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
                } else if (extent.bottom < this.restrictedExtent.bottom) {
                    lonlat = lonlat.add(0, this.restrictedExtent.bottom - extent.bottom);
                } else if (extent.top > this.restrictedExtent.top) {
                    lonlat = lonlat.add(0, this.restrictedExtent.top - extent.top);
                }
            }
        }
        var zoomChanged = forceZoomChange || ((this.isValidZoomLevel(zoom)) && (zoom != this.getZoom()));
        var centerChanged = (this.isValidLonLat(lonlat)) && (!lonlat.equals(this.center));
        if (zoomChanged || centerChanged || dragging) {
            dragging || this.events.triggerEvent("movestart");
            if (centerChanged) {
                if (!zoomChanged && this.center) {
                    this.centerLayerContainer(lonlat);
                }
                this.center = lonlat.clone();
            }
            var res = zoomChanged ? this.getResolutionForZoom(zoom) : this.getResolution();
            if (zoomChanged || this.layerContainerOrigin == null) {
                this.layerContainerOrigin = this.getCachedCenter();
                this.layerContainerDiv.style.left = "0px";
                this.layerContainerDiv.style.top = "0px";
                var maxExtent = this.getMaxExtent({
                    restricted: true
                });
                var maxExtentCenter = maxExtent.getCenterLonLat();
                var lonDelta = this.center.lon - maxExtentCenter.lon;
                var latDelta = maxExtentCenter.lat - this.center.lat;
                var extentWidth = Math.round(maxExtent.getWidth() / res);
                var extentHeight = Math.round(maxExtent.getHeight() / res);
                var left = (this.size.w - extentWidth) / 2 - lonDelta / res;
                var top = (this.size.h - extentHeight) / 2 - latDelta / res;
                this.minPx = new OpenLayers.Pixel(left, top);
                this.maxPx = new OpenLayers.Pixel(left + extentWidth, top + extentHeight);
            }
            if (zoomChanged) {
                this.zoom = zoom;
                this.resolution = res;
                this.viewRequestID++;
            }
            var bounds = this.getExtent();
            if (this.baseLayer.visibility) {
                this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
                options.dragging || this.baseLayer.events.triggerEvent("moveend", {
                    zoomChanged: zoomChanged
                });
            }
            bounds = this.baseLayer.getExtent();
            for (var i = this.layers.length - 1; i >= 0; --i) {
                var layer = this.layers[i];
                if (layer !== this.baseLayer && !layer.isBaseLayer) {
                    var inRange = layer.calculateInRange();
                    if (layer.inRange != inRange) {
                        layer.inRange = inRange;
                        if (!inRange) {
                            layer.display(false);
                        }
                        this.events.triggerEvent("changelayer", {
                            layer: layer,
                            property: "visibility"
                        });
                    }
                    if (inRange && layer.visibility) {
                        layer.moveTo(bounds, zoomChanged, options.dragging);
                        options.dragging || layer.events.triggerEvent("moveend", {
                            zoomChanged: zoomChanged
                        });
                    }
                }
            }
            this.events.triggerEvent("move");
            dragging || this.events.triggerEvent("moveend");
            if (zoomChanged) {
                for (var i = 0, len = this.popups.length; i < len; i++) {
                    this.popups[i].updatePosition();
                }
                this.events.triggerEvent("zoomend");
            }
        }
    },
    centerLayerContainer: function (lonlat) {
        var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin);
        var newPx = this.getViewPortPxFromLonLat(lonlat);
        if ((originPx != null) && (newPx != null)) {
            var oldLeft = parseInt(this.layerContainerDiv.style.left);
            var oldTop = parseInt(this.layerContainerDiv.style.top);
            var newLeft = Math.round(originPx.x - newPx.x);
            var newTop = Math.round(originPx.y - newPx.y);
            this.layerContainerDiv.style.left = newLeft + "px";
            this.layerContainerDiv.style.top = newTop + "px";
            var dx = oldLeft - newLeft;
            var dy = oldTop - newTop;
            this.minPx.x -= dx;
            this.maxPx.x -= dx;
            this.minPx.y -= dy;
            this.maxPx.y -= dy;
        }
    },
    isValidZoomLevel: function (zoomLevel) {
        return ((zoomLevel != null) && (zoomLevel >= 0) && (zoomLevel < this.getNumZoomLevels()));
    },
    isValidLonLat: function (lonlat) {
        var valid = false;
        if (lonlat != null) {
            var maxExtent = this.getMaxExtent();
            valid = maxExtent.containsLonLat(lonlat);
        }
        return valid;
    },
    getProjection: function () {
        var projection = this.getProjectionObject();
        return projection ? projection.getCode() : null;
    },
    getProjectionObject: function () {
        var projection = null;
        if (this.baseLayer != null) {
            projection = this.baseLayer.projection;
        }
        return projection;
    },
    getMaxResolution: function () {
        var maxResolution = null;
        if (this.baseLayer != null) {
            maxResolution = this.baseLayer.maxResolution;
        }
        return maxResolution;
    },
    getMaxExtent: function (options) {
        var maxExtent = null;
        if (options && options.restricted && this.restrictedExtent) {
            maxExtent = this.restrictedExtent;
        } else if (this.baseLayer != null) {
            maxExtent = this.baseLayer.maxExtent;
        }
        return maxExtent;
    },
    getNumZoomLevels: function () {
        var numZoomLevels = null;
        if (this.baseLayer != null) {
            numZoomLevels = this.baseLayer.numZoomLevels;
        }
        return numZoomLevels;
    },
    getExtent: function () {
        var extent = null;
        if (this.baseLayer != null) {
            extent = this.baseLayer.getExtent();
        }
        return extent;
    },
    getResolution: function () {
        var resolution = null;
        if (this.baseLayer != null) {
            resolution = this.baseLayer.getResolution();
        } else if (this.allOverlays === true && this.layers.length > 0) {
            resolution = this.layers[0].getResolution();
        }
        return resolution;
    },
    getUnits: function () {
        var units = null;
        if (this.baseLayer != null) {
            units = this.baseLayer.units;
        }
        return units;
    },
    getScale: function () {
        var scale = null;
        if (this.baseLayer != null) {
            var res = this.getResolution();
            var units = this.baseLayer.units;
            scale = OpenLayers.Util.getScaleFromResolution(res, units);
        }
        return scale;
    },
    getZoomForExtent: function (bounds, closest) {
        var zoom = null;
        if (this.baseLayer != null) {
            zoom = this.baseLayer.getZoomForExtent(bounds, closest);
        }
        return zoom;
    },
    getResolutionForZoom: function (zoom) {
        var resolution = null;
        if (this.baseLayer) {
            resolution = this.baseLayer.getResolutionForZoom(zoom);
        }
        return resolution;
    },
    getZoomForResolution: function (resolution, closest) {
        var zoom = null;
        if (this.baseLayer != null) {
            zoom = this.baseLayer.getZoomForResolution(resolution, closest);
        }
        return zoom;
    },
    zoomTo: function (zoom) {
        if (this.isValidZoomLevel(zoom)) {
            this.setCenter(null, zoom);
        }
    },
    zoomIn: function () {
        this.zoomTo(this.getZoom() + 1);
    },
    zoomOut: function () {
        this.zoomTo(this.getZoom() - 1);
    },
    zoomToExtent: function (bounds, closest) {
        var center = bounds.getCenterLonLat();
        if (this.baseLayer.wrapDateLine) {
            var maxExtent = this.getMaxExtent();
            bounds = bounds.clone();
            while (bounds.right < bounds.left) {
                bounds.right += maxExtent.getWidth();
            }
            center = bounds.getCenterLonLat().wrapDateLine(maxExtent);
        }
        this.setCenter(center, this.getZoomForExtent(bounds, closest));
    },
    zoomToMaxExtent: function (options) {
        var restricted = (options) ? options.restricted : true;
        var maxExtent = this.getMaxExtent({
            'restricted': restricted
        });
        this.zoomToExtent(maxExtent);
    },
    zoomToScale: function (scale, closest) {
        var res = OpenLayers.Util.getResolutionFromScale(scale, this.baseLayer.units);
        var size = this.getSize();
        var w_deg = size.w * res;
        var h_deg = size.h * res;
        var center = this.getCachedCenter();
        var extent = new OpenLayers.Bounds(center.lon - w_deg / 2, center.lat - h_deg / 2, center.lon + w_deg / 2, center.lat + h_deg / 2);
        this.zoomToExtent(extent, closest);
    },
    getLonLatFromViewPortPx: function (viewPortPx) {
        var lonlat = null;
        if (this.baseLayer != null) {
            lonlat = this.baseLayer.getLonLatFromViewPortPx(viewPortPx);
        }
        return lonlat;
    },
    getViewPortPxFromLonLat: function (lonlat) {
        var px = null;
        if (this.baseLayer != null) {
            px = this.baseLayer.getViewPortPxFromLonLat(lonlat);
        }
        return px;
    },
    getLonLatFromPixel: function (px) {
        return this.getLonLatFromViewPortPx(px);
    },
    getPixelFromLonLat: function (lonlat) {
        var px = this.getViewPortPxFromLonLat(lonlat);
        px.x = Math.round(px.x);
        px.y = Math.round(px.y);
        return px;
    },
    getGeodesicPixelSize: function (px) {
        var lonlat = px ? this.getLonLatFromPixel(px) : (this.getCachedCenter() || new OpenLayers.LonLat(0, 0));
        var res = this.getResolution();
        var left = lonlat.add(-res / 2, 0);
        var right = lonlat.add(res / 2, 0);
        var bottom = lonlat.add(0, -res / 2);
        var top = lonlat.add(0, res / 2);
        var dest = new OpenLayers.Projection("EPSG:4326");
        var source = this.getProjectionObject() || dest;
        if (!source.equals(dest)) {
            left.transform(source, dest);
            right.transform(source, dest);
            bottom.transform(source, dest);
            top.transform(source, dest);
        }
        return new OpenLayers.Size(OpenLayers.Util.distVincenty(left, right), OpenLayers.Util.distVincenty(bottom, top));
    },
    getViewPortPxFromLayerPx: function (layerPx) {
        var viewPortPx = null;
        if (layerPx != null) {
            var dX = parseInt(this.layerContainerDiv.style.left);
            var dY = parseInt(this.layerContainerDiv.style.top);
            viewPortPx = layerPx.add(dX, dY);
        }
        return viewPortPx;
    },
    getLayerPxFromViewPortPx: function (viewPortPx) {
        var layerPx = null;
        if (viewPortPx != null) {
            var dX = -parseInt(this.layerContainerDiv.style.left);
            var dY = -parseInt(this.layerContainerDiv.style.top);
            layerPx = viewPortPx.add(dX, dY);
            if (isNaN(layerPx.x) || isNaN(layerPx.y)) {
                layerPx = null;
            }
        }
        return layerPx;
    },
    getLonLatFromLayerPx: function (px) {
        px = this.getViewPortPxFromLayerPx(px);
        return this.getLonLatFromViewPortPx(px);
    },
    getLayerPxFromLonLat: function (lonlat) {
        var px = this.getPixelFromLonLat(lonlat);
        return this.getLayerPxFromViewPortPx(px);
    },
    CLASS_NAME: "OpenLayers.Map"
});
OpenLayers.Map.TILE_WIDTH = 256;
OpenLayers.Map.TILE_HEIGHT = 256;
OpenLayers.Projection = OpenLayers.Class({
    proj: null,
    projCode: null,
    titleRegEx: /\+title=[^\+]*/,
    initialize: function (projCode, options) {
        OpenLayers.Util.extend(this, options);
        this.projCode = projCode;
        if (window.Proj4js) {
            this.proj = new Proj4js.Proj(projCode);
        }
    },
    getCode: function () {
        return this.proj ? this.proj.srsCode : this.projCode;
    },
    getUnits: function () {
        return this.proj ? this.proj.units : null;
    },
    toString: function () {
        return this.getCode();
    },
    equals: function (projection) {
        var p = projection,
            equals = false;
        if (p) {
            if (window.Proj4js && this.proj.defData && p.proj.defData) {
                equals = this.proj.defData.replace(this.titleRegEx, "") == p.proj.defData.replace(this.titleRegEx, "");
            } else if (p.getCode) {
                var source = this.getCode(),
                    target = p.getCode();
                equals = source == target || !! OpenLayers.Projection.transforms[source] && OpenLayers.Projection.transforms[source][target] === OpenLayers.Projection.nullTransform;
            }
        }
        return equals;
    },
    destroy: function () {
        delete this.proj;
        delete this.projCode;
    },
    CLASS_NAME: "OpenLayers.Projection"
});
OpenLayers.Projection.transforms = {};
OpenLayers.Projection.addTransform = function (from, to, method) {
    if (!OpenLayers.Projection.transforms[from]) {
        OpenLayers.Projection.transforms[from] = {};
    }
    OpenLayers.Projection.transforms[from][to] = method;
};
OpenLayers.Projection.transform = function (point, source, dest) {
    if (source.proj && dest.proj) {
        point = Proj4js.transform(source.proj, dest.proj, point);
    } else if (source && dest && OpenLayers.Projection.transforms[source.getCode()] && OpenLayers.Projection.transforms[source.getCode()][dest.getCode()]) {
        OpenLayers.Projection.transforms[source.getCode()][dest.getCode()](point);
    }
    return point;
};
OpenLayers.Projection.nullTransform = function (point) {
    return point;
};
OpenLayers.Layer = OpenLayers.Class({
    id: null,
    name: null,
    div: null,
    opacity: null,
    alwaysInRange: null,
    EVENT_TYPES: ["loadstart", "loadend", "loadcancel", "visibilitychanged", "move", "moveend", "added", "removed"],
    RESOLUTION_PROPERTIES: ['scales', 'resolutions', 'maxScale', 'minScale', 'maxResolution', 'minResolution', 'numZoomLevels', 'maxZoomLevel'],
    events: null,
    map: null,
    isBaseLayer: false,
    alpha: false,
    displayInLayerSwitcher: true,
    visibility: true,
    attribution: null,
    inRange: false,
    imageSize: null,
    imageOffset: null,
    options: null,
    eventListeners: null,
    gutter: 0,
    projection: null,
    units: null,
    scales: null,
    resolutions: null,
    maxExtent: null,
    minExtent: null,
    maxResolution: null,
    minResolution: null,
    numZoomLevels: null,
    minScale: null,
    maxScale: null,
    displayOutsideMaxExtent: false,
    wrapDateLine: false,
    transitionEffect: null,
    SUPPORTED_TRANSITIONS: ['resize'],
    metadata: {},
    initialize: function (name, options) {
        this.addOptions(options);
        this.name = name;
        if (this.id == null) {
            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
            this.div = OpenLayers.Util.createDiv(this.id);
            this.div.style.width = "100%";
            this.div.style.height = "100%";
            this.div.dir = "ltr";
            this.events = new OpenLayers.Events(this, this.div, this.EVENT_TYPES);
            if (this.eventListeners instanceof Object) {
                this.events.on(this.eventListeners);
            }
        }
        if (this.wrapDateLine) {
            this.displayOutsideMaxExtent = true;
        }
    },
    destroy: function (setNewBaseLayer) {
        if (setNewBaseLayer == null) {
            setNewBaseLayer = true;
        }
        if (this.map != null) {
            this.map.removeLayer(this, setNewBaseLayer);
        }
        this.projection = null;
        this.map = null;
        this.name = null;
        this.div = null;
        this.options = null;
        if (this.events) {
            if (this.eventListeners) {
                this.events.un(this.eventListeners);
            }
            this.events.destroy();
        }
        this.eventListeners = null;
        this.events = null;
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer(this.name, this.getOptions());
        }
        OpenLayers.Util.applyDefaults(obj, this);
        obj.map = null;
        return obj;
    },
    getOptions: function () {
        var options = {};
        for (var o in this.options) {
            options[o] = this[o];
        }
        return options;
    },
    setName: function (newName) {
        if (newName != this.name) {
            this.name = newName;
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "name"
                });
            }
        }
    },
    addOptions: function (newOptions, reinitialize) {
        if (this.options == null) {
            this.options = {};
        }
        OpenLayers.Util.extend(this.options, newOptions);
        OpenLayers.Util.extend(this, newOptions);
        if (typeof this.projection == "string") {
            this.projection = new OpenLayers.Projection(this.projection);
        }
        if (this.projection && this.projection.getUnits()) {
            this.units = this.projection.getUnits();
        }
        if (this.map) {
            var resolution = this.map.getResolution();
            var properties = this.RESOLUTION_PROPERTIES.concat(["projection", "units", "minExtent", "maxExtent"]);
            for (var o in newOptions) {
                if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) {
                    this.initResolutions();
                    if (reinitialize && this.map.baseLayer === this) {
                        this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true);
                        this.map.events.triggerEvent("changebaselayer", {
                            layer: this
                        });
                    }
                    break;
                }
            }
        }
    },
    onMapResize: function () {},
    redraw: function () {
        var redrawn = false;
        if (this.map) {
            this.inRange = this.calculateInRange();
            var extent = this.getExtent();
            if (extent && this.inRange && this.visibility) {
                var zoomChanged = true;
                this.moveTo(extent, zoomChanged, false);
                this.events.triggerEvent("moveend", {
                    "zoomChanged": zoomChanged
                });
                redrawn = true;
            }
        }
        return redrawn;
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        var display = this.visibility;
        if (!this.isBaseLayer) {
            display = display && this.inRange;
        }
        this.display(display);
    },
    moveByPx: function (dx, dy) {},
    setMap: function (map) {
        if (this.map == null) {
            this.map = map;
            this.maxExtent = this.maxExtent || this.map.maxExtent;
            this.minExtent = this.minExtent || this.map.minExtent;
            this.projection = this.projection || this.map.projection;
            if (typeof this.projection == "string") {
                this.projection = new OpenLayers.Projection(this.projection);
            }
            this.units = this.projection.getUnits() || this.units || this.map.units;
            this.initResolutions();
            if (!this.isBaseLayer) {
                this.inRange = this.calculateInRange();
                var show = ((this.visibility) && (this.inRange));
                this.div.style.display = show ? "" : "none";
            }
            this.setTileSize();
        }
    },
    afterAdd: function () {},
    removeMap: function (map) {},
    getImageSize: function (bounds) {
        return (this.imageSize || this.tileSize);
    },
    setTileSize: function (size) {
        var tileSize = (size) ? size : ((this.tileSize) ? this.tileSize : this.map.getTileSize());
        this.tileSize = tileSize;
        if (this.gutter) {
            this.imageOffset = new OpenLayers.Pixel(-this.gutter, -this.gutter);
            this.imageSize = new OpenLayers.Size(tileSize.w + (2 * this.gutter), tileSize.h + (2 * this.gutter));
        }
    },
    getVisibility: function () {
        return this.visibility;
    },
    setVisibility: function (visibility) {
        if (visibility != this.visibility) {
            this.visibility = visibility;
            this.display(visibility);
            this.redraw();
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "visibility"
                });
            }
            this.events.triggerEvent("visibilitychanged");
        }
    },
    display: function (display) {
        if (display != (this.div.style.display != "none")) {
            this.div.style.display = (display && this.calculateInRange()) ? "block" : "none";
        }
    },
    calculateInRange: function () {
        var inRange = false;
        if (this.alwaysInRange) {
            inRange = true;
        } else {
            if (this.map) {
                var resolution = this.map.getResolution();
                inRange = ((resolution >= this.minResolution) && (resolution <= this.maxResolution));
            }
        }
        return inRange;
    },
    setIsBaseLayer: function (isBaseLayer) {
        if (isBaseLayer != this.isBaseLayer) {
            this.isBaseLayer = isBaseLayer;
            if (this.map != null) {
                this.map.events.triggerEvent("changebaselayer", {
                    layer: this
                });
            }
        }
    },
    initResolutions: function () {
        var i, len, p;
        var props = {},
            alwaysInRange = true;
        for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {
            p = this.RESOLUTION_PROPERTIES[i];
            props[p] = this.options[p];
            if (alwaysInRange && this.options[p]) {
                alwaysInRange = false;
            }
        }
        if (this.alwaysInRange == null) {
            this.alwaysInRange = alwaysInRange;
        }
        if (props.resolutions == null) {
            props.resolutions = this.resolutionsFromScales(props.scales);
        }
        if (props.resolutions == null) {
            props.resolutions = this.calculateResolutions(props);
        }
        if (props.resolutions == null) {
            for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) {
                p = this.RESOLUTION_PROPERTIES[i];
                props[p] = this.options[p] != null ? this.options[p] : this.map[p];
            }
            if (props.resolutions == null) {
                props.resolutions = this.resolutionsFromScales(props.scales);
            }
            if (props.resolutions == null) {
                props.resolutions = this.calculateResolutions(props);
            }
        }
        var maxResolution;
        if (this.options.maxResolution && this.options.maxResolution !== "auto") {
            maxResolution = this.options.maxResolution;
        }
        if (this.options.minScale) {
            maxResolution = OpenLayers.Util.getResolutionFromScale(this.options.minScale, this.units);
        }
        var minResolution;
        if (this.options.minResolution && this.options.minResolution !== "auto") {
            minResolution = this.options.minResolution;
        }
        if (this.options.maxScale) {
            minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units);
        }
        if (props.resolutions) {
            props.resolutions.sort(function (a, b) {
                return (b - a);
            });
            if (!maxResolution) {
                maxResolution = props.resolutions[0];
            }
            if (!minResolution) {
                var lastIdx = props.resolutions.length - 1;
                minResolution = props.resolutions[lastIdx];
            }
        }
        this.resolutions = props.resolutions;
        if (this.resolutions) {
            len = this.resolutions.length;
            this.scales = new Array(len);
            for (i = 0; i < len; i++) {
                this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units);
            }
            this.numZoomLevels = len;
        }
        this.minResolution = minResolution;
        if (minResolution) {
            this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units);
        }
        this.maxResolution = maxResolution;
        if (maxResolution) {
            this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units);
        }
    },
    resolutionsFromScales: function (scales) {
        if (scales == null) {
            return;
        }
        var resolutions, i, len;
        len = scales.length;
        resolutions = new Array(len);
        for (i = 0; i < len; i++) {
            resolutions[i] = OpenLayers.Util.getResolutionFromScale(scales[i], this.units);
        }
        return resolutions;
    },
    calculateResolutions: function (props) {
        var viewSize, wRes, hRes;
        var maxResolution = props.maxResolution;
        if (props.minScale != null) {
            maxResolution = OpenLayers.Util.getResolutionFromScale(props.minScale, this.units);
        } else if (maxResolution == "auto" && this.maxExtent != null) {
            viewSize = this.map.getSize();
            wRes = this.maxExtent.getWidth() / viewSize.w;
            hRes = this.maxExtent.getHeight() / viewSize.h;
            maxResolution = Math.max(wRes, hRes);
        }
        var minResolution = props.minResolution;
        if (props.maxScale != null) {
            minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units);
        } else if (props.minResolution == "auto" && this.minExtent != null) {
            viewSize = this.map.getSize();
            wRes = this.minExtent.getWidth() / viewSize.w;
            hRes = this.minExtent.getHeight() / viewSize.h;
            minResolution = Math.max(wRes, hRes);
        }
        var maxZoomLevel = props.maxZoomLevel;
        var numZoomLevels = props.numZoomLevels;
        if (typeof minResolution === "number" && typeof maxResolution === "number" && numZoomLevels === undefined) {
            var ratio = maxResolution / minResolution;
            numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1;
        } else if (numZoomLevels === undefined && maxZoomLevel != null) {
            numZoomLevels = maxZoomLevel + 1;
        }
        if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || (typeof maxResolution !== "number" && typeof minResolution !== "number")) {
            return;
        }
        var resolutions = new Array(numZoomLevels);
        var base = 2;
        if (typeof minResolution == "number" && typeof maxResolution == "number") {
            base = Math.pow((maxResolution / minResolution), (1 / (numZoomLevels - 1)));
        }
        var i;
        if (typeof maxResolution === "number") {
            for (i = 0; i < numZoomLevels; i++) {
                resolutions[i] = maxResolution / Math.pow(base, i);
            }
        } else {
            for (i = 0; i < numZoomLevels; i++) {
                resolutions[numZoomLevels - 1 - i] = minResolution * Math.pow(base, i);
            }
        }
        return resolutions;
    },
    getResolution: function () {
        var zoom = this.map.getZoom();
        return this.getResolutionForZoom(zoom);
    },
    getExtent: function () {
        return this.map.calculateBounds();
    },
    getZoomForExtent: function (extent, closest) {
        var viewSize = this.map.getSize();
        var idealResolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);
        return this.getZoomForResolution(idealResolution, closest);
    },
    getDataExtent: function () {},
    getResolutionForZoom: function (zoom) {
        zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1));
        var resolution;
        if (this.map.fractionalZoom) {
            var low = Math.floor(zoom);
            var high = Math.ceil(zoom);
            resolution = this.resolutions[low] - ((zoom - low) * (this.resolutions[low] - this.resolutions[high]));
        } else {
            resolution = this.resolutions[Math.round(zoom)];
        }
        return resolution;
    },
    getZoomForResolution: function (resolution, closest) {
        var zoom, i, len;
        if (this.map.fractionalZoom) {
            var lowZoom = 0;
            var highZoom = this.resolutions.length - 1;
            var highRes = this.resolutions[lowZoom];
            var lowRes = this.resolutions[highZoom];
            var res;
            for (i = 0, len = this.resolutions.length; i < len; ++i) {
                res = this.resolutions[i];
                if (res >= resolution) {
                    highRes = res;
                    lowZoom = i;
                }
                if (res <= resolution) {
                    lowRes = res;
                    highZoom = i;
                    break;
                }
            }
            var dRes = highRes - lowRes;
            if (dRes > 0) {
                zoom = lowZoom + ((highRes - resolution) / dRes);
            } else {
                zoom = lowZoom;
            }
        } else {
            var diff;
            var minDiff = Number.POSITIVE_INFINITY;
            for (i = 0, len = this.resolutions.length; i < len; i++) {
                if (closest) {
                    diff = Math.abs(this.resolutions[i] - resolution);
                    if (diff > minDiff) {
                        break;
                    }
                    minDiff = diff;
                } else {
                    if (this.resolutions[i] < resolution) {
                        break;
                    }
                }
            }
            zoom = Math.max(0, i - 1);
        }
        return zoom;
    },
    getLonLatFromViewPortPx: function (viewPortPx) {
        var lonlat = null;
        var map = this.map;
        if (viewPortPx != null && map.minPx) {
            var res = map.getResolution();
            var maxExtent = map.getMaxExtent({
                restricted: true
            });
            var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
            var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
            lonlat = new OpenLayers.LonLat(lon, lat);
            if (this.wrapDateLine) {
                lonlat = lonlat.wrapDateLine(this.maxExtent);
            }
        }
        return lonlat;
    },
    getViewPortPxFromLonLat: function (lonlat) {
        var px = null;
        if (lonlat != null) {
            var resolution = this.map.getResolution();
            var extent = this.map.getExtent();
            px = new OpenLayers.Pixel((1 / resolution * (lonlat.lon - extent.left)), (1 / resolution * (extent.top - lonlat.lat)));
        }
        return px;
    },
    setOpacity: function (opacity) {
        if (opacity != this.opacity) {
            this.opacity = opacity;
            for (var i = 0, len = this.div.childNodes.length; i < len; ++i) {
                var element = this.div.childNodes[i].firstChild;
                OpenLayers.Util.modifyDOMElement(element, null, null, null, null, null, null, opacity);
            }
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "opacity"
                });
            }
        }
    },
    getZIndex: function () {
        return this.div.style.zIndex;
    },
    setZIndex: function (zIndex) {
        this.div.style.zIndex = zIndex;
    },
    adjustBounds: function (bounds) {
        if (this.gutter) {
            var mapGutter = this.gutter * this.map.getResolution();
            bounds = new OpenLayers.Bounds(bounds.left - mapGutter, bounds.bottom - mapGutter, bounds.right + mapGutter, bounds.top + mapGutter);
        }
        if (this.wrapDateLine) {
            var wrappingOptions = {
                'rightTolerance': this.getResolution(),
                'leftTolerance': this.getResolution()
            };
            bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
        }
        return bounds;
    },
    CLASS_NAME: "OpenLayers.Layer"
});
OpenLayers.Layer.SphericalMercator = {
    getExtent: function () {
        var extent = null;
        if (this.sphericalMercator) {
            extent = this.map.calculateBounds();
        } else {
            extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this);
        }
        return extent;
    },
    getLonLatFromViewPortPx: function (viewPortPx) {
        return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments);
    },
    getViewPortPxFromLonLat: function (lonlat) {
        return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments);
    },
    initMercatorParameters: function () {
        this.RESOLUTIONS = [];
        var maxResolution = 156543.03390625;
        for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) {
            this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom);
        }
        this.units = "m";
        this.projection = this.projection || "EPSG:900913";
    },
    forwardMercator: function (lon, lat) {
        var x = lon * 20037508.34 / 180;
        var y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
        y = y * 20037508.34 / 180;
        return new OpenLayers.LonLat(x, y);
    },
    inverseMercator: function (x, y) {
        var lon = (x / 20037508.34) * 180;
        var lat = (y / 20037508.34) * 180;
        lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
        return new OpenLayers.LonLat(lon, lat);
    },
    projectForward: function (point) {
        var lonlat = OpenLayers.Layer.SphericalMercator.forwardMercator(point.x, point.y);
        point.x = lonlat.lon;
        point.y = lonlat.lat;
        return point;
    },
    projectInverse: function (point) {
        var lonlat = OpenLayers.Layer.SphericalMercator.inverseMercator(point.x, point.y);
        point.x = lonlat.lon;
        point.y = lonlat.lat;
        return point;
    }
};
(function () {
    var codes = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"];
    var add = OpenLayers.Projection.addTransform;
    var merc = OpenLayers.Layer.SphericalMercator;
    var same = OpenLayers.Projection.nullTransform;
    var i, len, code, other, j;
    for (i = 0, len = codes.length; i < len; ++i) {
        code = codes[i];
        add("EPSG:4326", code, merc.projectForward);
        add(code, "EPSG:4326", merc.projectInverse);
        for (j = i + 1; j < len; ++j) {
            other = codes[j];
            add(code, other, same);
            add(other, code, same);
        }
    }
})();
OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, {
    smoothDragPan: true,
    isBaseLayer: true,
    isFixed: true,
    pane: null,
    mapObject: null,
    initialize: function (name, options) {
        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
        if (this.pane == null) {
            this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane");
        }
    },
    destroy: function () {
        this.mapObject = null;
        this.pane = null;
        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
    },
    setMap: function (map) {
        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
        this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
        this.pane.style.display = this.div.style.display;
        this.pane.style.width = "100%";
        this.pane.style.height = "100%";
        if (OpenLayers.BROWSER_NAME == "msie") {
            this.pane.style.background = "url(" + OpenLayers.Util.getImagesLocation() + "blank.gif)";
        }
        if (this.isFixed) {
            this.map.eventsDiv.appendChild(this.pane);
        } else {
            this.map.layerContainerDiv.appendChild(this.pane);
        }
        this.loadMapObject();
        if (this.mapObject == null) {
            this.loadWarningMessage();
        }
    },
    removeMap: function (map) {
        if (this.pane && this.pane.parentNode) {
            this.pane.parentNode.removeChild(this.pane);
        }
        OpenLayers.Layer.prototype.removeMap.apply(this, arguments);
    },
    loadWarningMessage: function () {
        this.div.style.backgroundColor = "darkblue";
        var viewSize = this.map.getSize();
        var msgW = Math.min(viewSize.w, 300);
        var msgH = Math.min(viewSize.h, 200);
        var size = new OpenLayers.Size(msgW, msgH);
        var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2);
        var topLeft = centerPx.add(-size.w / 2, -size.h / 2);
        var div = OpenLayers.Util.createDiv(this.name + "_warning", topLeft, size, null, null, null, "auto");
        div.style.padding = "7px";
        div.style.backgroundColor = "yellow";
        div.innerHTML = this.getWarningHTML();
        this.div.appendChild(div);
    },
    getWarningHTML: function () {
        return "";
    },
    display: function (display) {
        OpenLayers.Layer.prototype.display.apply(this, arguments);
        this.pane.style.display = this.div.style.display;
    },
    setZIndex: function (zIndex) {
        OpenLayers.Layer.prototype.setZIndex.apply(this, arguments);
        this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1;
    },
    moveByPx: function (dx, dy) {
        OpenLayers.Layer.prototype.moveByPx.apply(this, arguments);
        if (this.dragPanMapObject) {
            this.dragPanMapObject(dx, -dy);
        } else {
            this.moveTo(this.map.getCachedCenter());
        }
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
        if (this.mapObject != null) {
            var newCenter = this.map.getCenter();
            var newZoom = this.map.getZoom();
            if (newCenter != null) {
                var moOldCenter = this.getMapObjectCenter();
                var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter);
                var moOldZoom = this.getMapObjectZoom();
                var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom);
                if (!(newCenter.equals(oldCenter)) || !(newZoom == oldZoom)) {
                    if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) {
                        var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
                        var newPx = this.map.getViewPortPxFromLonLat(newCenter);
                        this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y);
                    } else {
                        var center = this.getMapObjectLonLatFromOLLonLat(newCenter);
                        var zoom = this.getMapObjectZoomFromOLZoom(newZoom);
                        this.setMapObjectCenter(center, zoom, dragging);
                    }
                }
            }
        }
    },
    getLonLatFromViewPortPx: function (viewPortPx) {
        var lonlat = null;
        if ((this.mapObject != null) && (this.getMapObjectCenter() != null)) {
            var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx);
            var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel);
            lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat);
        }
        return lonlat;
    },
    getViewPortPxFromLonLat: function (lonlat) {
        var viewPortPx = null;
        if ((this.mapObject != null) && (this.getMapObjectCenter() != null)) {
            var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat);
            var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat);
            viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel);
        }
        return viewPortPx;
    },
    getOLLonLatFromMapObjectLonLat: function (moLonLat) {
        var olLonLat = null;
        if (moLonLat != null) {
            var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
            var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
            olLonLat = new OpenLayers.LonLat(lon, lat);
        }
        return olLonLat;
    },
    getMapObjectLonLatFromOLLonLat: function (olLonLat) {
        var moLatLng = null;
        if (olLonLat != null) {
            moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat);
        }
        return moLatLng;
    },
    getOLPixelFromMapObjectPixel: function (moPixel) {
        var olPixel = null;
        if (moPixel != null) {
            var x = this.getXFromMapObjectPixel(moPixel);
            var y = this.getYFromMapObjectPixel(moPixel);
            olPixel = new OpenLayers.Pixel(x, y);
        }
        return olPixel;
    },
    getMapObjectPixelFromOLPixel: function (olPixel) {
        var moPixel = null;
        if (olPixel != null) {
            moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y);
        }
        return moPixel;
    },
    CLASS_NAME: "OpenLayers.Layer.EventPane"
});
OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({
    initialize: function () {},
    initResolutions: function () {
        var props = new Array('minZoomLevel', 'maxZoomLevel', 'numZoomLevels');
        for (var i = 0, len = props.length; i < len; i++) {
            var property = props[i];
            this[property] = (this.options[property] != null) ? this.options[property] : this.map[property];
        }
        if ((this.minZoomLevel == null) || (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) {
            this.minZoomLevel = this.MIN_ZOOM_LEVEL;
        }
        var desiredZoomLevels;
        var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1;
        if (((this.options.numZoomLevels == null) && (this.options.maxZoomLevel != null)) || ((this.numZoomLevels == null) && (this.maxZoomLevel != null))) {
            desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1;
        } else {
            desiredZoomLevels = this.numZoomLevels;
        }
        if (desiredZoomLevels != null) {
            this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels);
        } else {
            this.numZoomLevels = limitZoomLevels;
        }
        this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1;
        if (this.RESOLUTIONS != null) {
            var resolutionsIndex = 0;
            this.resolutions = [];
            for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) {
                this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i];
            }
            this.maxResolution = this.resolutions[0];
            this.minResolution = this.resolutions[this.resolutions.length - 1];
        }
    },
    getResolution: function () {
        if (this.resolutions != null) {
            return OpenLayers.Layer.prototype.getResolution.apply(this, arguments);
        } else {
            var resolution = null;
            var viewSize = this.map.getSize();
            var extent = this.getExtent();
            if ((viewSize != null) && (extent != null)) {
                resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h);
            }
            return resolution;
        }
    },
    getExtent: function () {
        var extent = null;
        var size = this.map.getSize();
        var tlPx = new OpenLayers.Pixel(0, 0);
        var tlLL = this.getLonLatFromViewPortPx(tlPx);
        var brPx = new OpenLayers.Pixel(size.w, size.h);
        var brLL = this.getLonLatFromViewPortPx(brPx);
        if ((tlLL != null) && (brLL != null)) {
            extent = new OpenLayers.Bounds(tlLL.lon, brLL.lat, brLL.lon, tlLL.lat);
        }
        return extent;
    },
    getZoomForResolution: function (resolution) {
        if (this.resolutions != null) {
            return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments);
        } else {
            var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []);
            return this.getZoomForExtent(extent);
        }
    },
    getOLZoomFromMapObjectZoom: function (moZoom) {
        var zoom = null;
        if (moZoom != null) {
            zoom = moZoom - this.minZoomLevel;
            if (this.map.baseLayer !== this) {
                zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom))
            }
        }
        return zoom;
    },
    getMapObjectZoomFromOLZoom: function (olZoom) {
        var zoom = null;
        if (olZoom != null) {
            zoom = olZoom + this.minZoomLevel;
            if (this.map.baseLayer !== this) {
                zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom));
            }
        }
        return zoom;
    },
    CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels"
});
OpenLayers.Layer.VirtualEarth = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
    MIN_ZOOM_LEVEL: 1,
    MAX_ZOOM_LEVEL: 19,
    RESOLUTIONS: [1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 0.0000858306884765625, 0.00004291534423828125, 0.00002145767211914062, 0.00001072883605957031, 0.00000536441802978515],
    type: null,
    wrapDateLine: true,
    sphericalMercator: false,
    animationEnabled: true,
    initialize: function (name, options) {
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, arguments);
        if (this.sphericalMercator) {
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
            this.initMercatorParameters();
        }
    },
    loadMapObject: function () {
        var veDiv = OpenLayers.Util.createDiv(this.name);
        var sz = this.map.getSize();
        veDiv.style.width = sz.w + "px";
        veDiv.style.height = sz.h + "px";
        this.div.appendChild(veDiv);
        try {
            this.mapObject = new VEMap(this.name);
        } catch (e) {}
        if (this.mapObject != null) {
            try {
                this.mapObject.LoadMap(null, null, this.type, true);
                this.mapObject.AttachEvent("onmousedown", OpenLayers.Function.True);
            } catch (e) {}
            this.mapObject.HideDashboard();
            if (typeof this.mapObject.SetAnimationEnabled == "function") {
                this.mapObject.SetAnimationEnabled(this.animationEnabled);
            }
        }
        if (!this.mapObject || !this.mapObject.vemapcontrol || !this.mapObject.vemapcontrol.PanMap || (typeof this.mapObject.vemapcontrol.PanMap != "function")) {
            this.dragPanMapObject = null;
        }
    },
    onMapResize: function () {
        this.mapObject.Resize(this.map.size.w, this.map.size.h);
    },
    getWarningHTML: function () {
        return OpenLayers.i18n("getLayerWarning", {
            'layerType': 'VE',
            'layerLib': 'VirtualEarth'
        });
    },
    setMapObjectCenter: function (center, zoom) {
        this.mapObject.SetCenterAndZoom(center, zoom);
    },
    getMapObjectCenter: function () {
        return this.mapObject.GetCenter();
    },
    dragPanMapObject: function (dX, dY) {
        this.mapObject.vemapcontrol.PanMap(dX, -dY);
    },
    getMapObjectZoom: function () {
        return this.mapObject.GetZoomLevel();
    },
    getMapObjectLonLatFromMapObjectPixel: function (moPixel) {
        return (typeof VEPixel != 'undefined') ? this.mapObject.PixelToLatLong(moPixel) : this.mapObject.PixelToLatLong(moPixel.x, moPixel.y);
    },
    getMapObjectPixelFromMapObjectLonLat: function (moLonLat) {
        return this.mapObject.LatLongToPixel(moLonLat);
    },
    getLongitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lon : moLonLat.Longitude;
    },
    getLatitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.Longitude, moLonLat.Latitude).lat : moLonLat.Latitude;
    },
    getMapObjectLonLatFromLonLat: function (lon, lat) {
        var veLatLong;
        if (this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            veLatLong = new VELatLong(lonlat.lat, lonlat.lon);
        } else {
            veLatLong = new VELatLong(lat, lon);
        }
        return veLatLong;
    },
    getXFromMapObjectPixel: function (moPixel) {
        return moPixel.x;
    },
    getYFromMapObjectPixel: function (moPixel) {
        return moPixel.y;
    },
    getMapObjectPixelFromXY: function (x, y) {
        return (typeof VEPixel != 'undefined') ? new VEPixel(x, y) : new Msn.VE.Pixel(x, y);
    },
    CLASS_NAME: "OpenLayers.Layer.VirtualEarth"
});
OpenLayers.Control = OpenLayers.Class({
    id: null,
    map: null,
    div: null,
    type: null,
    allowSelection: false,
    displayClass: "",
    title: "",
    autoActivate: false,
    active: null,
    handler: null,
    eventListeners: null,
    events: null,
    EVENT_TYPES: ["activate", "deactivate"],
    initialize: function (options) {
        this.displayClass = this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, "");
        OpenLayers.Util.extend(this, options);
        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
        if (this.eventListeners instanceof Object) {
            this.events.on(this.eventListeners);
        }
        if (this.id == null) {
            this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
        }
    },
    destroy: function () {
        if (this.events) {
            if (this.eventListeners) {
                this.events.un(this.eventListeners);
            }
            this.events.destroy();
            this.events = null;
        }
        this.eventListeners = null;
        if (this.handler) {
            this.handler.destroy();
            this.handler = null;
        }
        if (this.handlers) {
            for (var key in this.handlers) {
                if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == "function") {
                    this.handlers[key].destroy();
                }
            }
            this.handlers = null;
        }
        if (this.map) {
            this.map.removeControl(this);
            this.map = null;
        }
        this.div = null;
    },
    setMap: function (map) {
        this.map = map;
        if (this.handler) {
            this.handler.setMap(map);
        }
    },
    draw: function (px) {
        if (this.div == null) {
            this.div = OpenLayers.Util.createDiv(this.id);
            this.div.className = this.displayClass;
            if (!this.allowSelection) {
                this.div.className += " olControlNoSelect";
                this.div.setAttribute("unselectable", "on", 0);
                this.div.onselectstart = OpenLayers.Function.False;
            }
            if (this.title != "") {
                this.div.title = this.title;
            }
        }
        if (px != null) {
            this.position = px.clone();
        }
        this.moveTo(this.position);
        return this.div;
    },
    moveTo: function (px) {
        if ((px != null) && (this.div != null)) {
            this.div.style.left = px.x + "px";
            this.div.style.top = px.y + "px";
        }
    },
    activate: function () {
        if (this.active) {
            return false;
        }
        if (this.handler) {
            this.handler.activate();
        }
        this.active = true;
        if (this.map) {
            OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active");
        }
        this.events.triggerEvent("activate");
        return true;
    },
    deactivate: function () {
        if (this.active) {
            if (this.handler) {
                this.handler.deactivate();
            }
            this.active = false;
            if (this.map) {
                OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active");
            }
            this.events.triggerEvent("deactivate");
            return true;
        }
        return false;
    },
    CLASS_NAME: "OpenLayers.Control"
});
OpenLayers.Control.TYPE_BUTTON = 1;
OpenLayers.Control.TYPE_TOGGLE = 2;
OpenLayers.Control.TYPE_TOOL = 3;
OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, {
    element: null,
    ovmap: null,
    size: new OpenLayers.Size(180, 90),
    layers: null,
    minRectSize: 15,
    minRectDisplayClass: "RectReplacement",
    minRatio: 8,
    maxRatio: 32,
    mapOptions: null,
    autoPan: false,
    handlers: null,
    resolutionFactor: 1,
    maximized: false,
    initialize: function (options) {
        this.layers = [];
        this.handlers = {};
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
    },
    destroy: function () {
        if (!this.mapDiv) {
            return;
        }
        if (this.handlers.click) {
            this.handlers.click.destroy();
        }
        if (this.handlers.drag) {
            this.handlers.drag.destroy();
        }
        this.ovmap && this.ovmap.eventsDiv.removeChild(this.extentRectangle);
        this.extentRectangle = null;
        if (this.rectEvents) {
            this.rectEvents.destroy();
            this.rectEvents = null;
        }
        if (this.ovmap) {
            this.ovmap.destroy();
            this.ovmap = null;
        }
        this.element.removeChild(this.mapDiv);
        this.mapDiv = null;
        this.div.removeChild(this.element);
        this.element = null;
        if (this.maximizeDiv) {
            OpenLayers.Event.stopObservingElement(this.maximizeDiv);
            this.div.removeChild(this.maximizeDiv);
            this.maximizeDiv = null;
        }
        if (this.minimizeDiv) {
            OpenLayers.Event.stopObservingElement(this.minimizeDiv);
            this.div.removeChild(this.minimizeDiv);
            this.minimizeDiv = null;
        }
        this.map.events.un({
            "moveend": this.update,
            "changebaselayer": this.baseLayerDraw,
            scope: this
        });
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!(this.layers.length > 0)) {
            if (this.map.baseLayer) {
                var layer = this.map.baseLayer.clone();
                this.layers = [layer];
            } else {
                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
                return this.div;
            }
        }
        this.element = document.createElement('div');
        this.element.className = this.displayClass + 'Element';
        this.element.style.display = 'none';
        this.mapDiv = document.createElement('div');
        this.mapDiv.style.width = this.size.w + 'px';
        this.mapDiv.style.height = this.size.h + 'px';
        this.mapDiv.style.position = 'relative';
        this.mapDiv.style.overflow = 'hidden';
        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
        this.extentRectangle = document.createElement('div');
        this.extentRectangle.style.position = 'absolute';
        this.extentRectangle.style.zIndex = 1000;
        this.extentRectangle.className = this.displayClass + 'ExtentRectangle';
        this.element.appendChild(this.mapDiv);
        this.div.appendChild(this.element);
        if (!this.outsideViewport) {
            this.div.className += " " + this.displayClass + 'Container';
            var imgLocation = OpenLayers.Util.getImagesLocation();
            var img = imgLocation + 'layer-switcher-maximize.png';
            this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(this.displayClass + 'MaximizeButton', null, new OpenLayers.Size(18, 18), img, 'absolute');
            this.maximizeDiv.style.display = 'none';
            this.maximizeDiv.className = this.displayClass + 'MaximizeButton';
            OpenLayers.Event.observe(this.maximizeDiv, 'click', OpenLayers.Function.bindAsEventListener(this.maximizeControl, this));
            this.div.appendChild(this.maximizeDiv);
            var img = imgLocation + 'layer-switcher-minimize.png';
            this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv('OpenLayers_Control_minimizeDiv', null, new OpenLayers.Size(18, 18), img, 'absolute');
            this.minimizeDiv.style.display = 'none';
            this.minimizeDiv.className = this.displayClass + 'MinimizeButton';
            OpenLayers.Event.observe(this.minimizeDiv, 'click', OpenLayers.Function.bindAsEventListener(this.minimizeControl, this));
            this.div.appendChild(this.minimizeDiv);
            var eventsToStop = ['dblclick', 'mousedown'];
            for (var i = 0, len = eventsToStop.length; i < len; i++) {
                OpenLayers.Event.observe(this.maximizeDiv, eventsToStop[i], OpenLayers.Event.stop);
                OpenLayers.Event.observe(this.minimizeDiv, eventsToStop[i], OpenLayers.Event.stop);
            }
            this.minimizeControl();
        } else {
            this.element.style.display = '';
        }
        if (this.map.getExtent()) {
            this.update();
        }
        this.map.events.register('moveend', this, this.update);
        if (this.maximized) {
            this.maximizeControl();
        }
        return this.div;
    },
    baseLayerDraw: function () {
        this.draw();
        this.map.events.unregister("changebaselayer", this, this.baseLayerDraw);
    },
    rectDrag: function (px) {
        var deltaX = this.handlers.drag.last.x - px.x;
        var deltaY = this.handlers.drag.last.y - px.y;
        if (deltaX != 0 || deltaY != 0) {
            var rectTop = this.rectPxBounds.top;
            var rectLeft = this.rectPxBounds.left;
            var rectHeight = Math.abs(this.rectPxBounds.getHeight());
            var rectWidth = this.rectPxBounds.getWidth();
            var newTop = Math.max(0, (rectTop - deltaY));
            newTop = Math.min(newTop, this.ovmap.size.h - this.hComp - rectHeight);
            var newLeft = Math.max(0, (rectLeft - deltaX));
            newLeft = Math.min(newLeft, this.ovmap.size.w - this.wComp - rectWidth);
            this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + rectHeight, newLeft + rectWidth, newTop));
        }
    },
    mapDivClick: function (evt) {
        var pxCenter = this.rectPxBounds.getCenterPixel();
        var deltaX = evt.xy.x - pxCenter.x;
        var deltaY = evt.xy.y - pxCenter.y;
        var top = this.rectPxBounds.top;
        var left = this.rectPxBounds.left;
        var height = Math.abs(this.rectPxBounds.getHeight());
        var width = this.rectPxBounds.getWidth();
        var newTop = Math.max(0, (top + deltaY));
        newTop = Math.min(newTop, this.ovmap.size.h - height);
        var newLeft = Math.max(0, (left + deltaX));
        newLeft = Math.min(newLeft, this.ovmap.size.w - width);
        this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + height, newLeft + width, newTop));
        this.updateMapToRect();
    },
    maximizeControl: function (e) {
        this.element.style.display = '';
        this.showToggle(false);
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },
    minimizeControl: function (e) {
        this.element.style.display = 'none';
        this.showToggle(true);
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },
    showToggle: function (minimize) {
        this.maximizeDiv.style.display = minimize ? '' : 'none';
        this.minimizeDiv.style.display = minimize ? 'none' : '';
    },
    update: function () {
        if (this.ovmap == null) {
            this.createMap();
        }
        if (this.autoPan || !this.isSuitableOverview()) {
            this.updateOverview();
        }
        this.updateRectToMap();
    },
    isSuitableOverview: function () {
        var mapExtent = this.map.getExtent();
        var maxExtent = this.map.maxExtent;
        var testExtent = new OpenLayers.Bounds(Math.max(mapExtent.left, maxExtent.left), Math.max(mapExtent.bottom, maxExtent.bottom), Math.min(mapExtent.right, maxExtent.right), Math.min(mapExtent.top, maxExtent.top));
        if (this.ovmap.getProjection() != this.map.getProjection()) {
            testExtent = testExtent.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject());
        }
        var resRatio = this.ovmap.getResolution() / this.map.getResolution();
        return ((resRatio > this.minRatio) && (resRatio <= this.maxRatio) && (this.ovmap.getExtent().containsBounds(testExtent)));
    },
    updateOverview: function () {
        var mapRes = this.map.getResolution();
        var targetRes = this.ovmap.getResolution();
        var resRatio = targetRes / mapRes;
        if (resRatio > this.maxRatio) {
            targetRes = this.minRatio * mapRes;
        } else if (resRatio <= this.minRatio) {
            targetRes = this.maxRatio * mapRes;
        }
        var center;
        if (this.ovmap.getProjection() != this.map.getProjection()) {
            center = this.map.center.clone();
            center.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject());
        } else {
            center = this.map.center;
        }
        this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(targetRes * this.resolutionFactor));
        this.updateRectToMap();
    },
    createMap: function () {
        var options = OpenLayers.Util.extend({
            controls: [],
            maxResolution: 'auto',
            fallThrough: false
        }, this.mapOptions);
        this.ovmap = new OpenLayers.Map(this.mapDiv, options);
        this.ovmap.eventsDiv.appendChild(this.extentRectangle);
        OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy);
        this.ovmap.addLayers(this.layers);
        this.ovmap.zoomToMaxExtent();
        this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-left-width')) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-right-width'));
        this.wComp = (this.wComp) ? this.wComp : 2;
        this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-top-width')) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, 'border-bottom-width'));
        this.hComp = (this.hComp) ? this.hComp : 2;
        this.handlers.drag = new OpenLayers.Handler.Drag(this, {
            move: this.rectDrag,
            done: this.updateMapToRect
        }, {
            map: this.ovmap
        });
        this.handlers.click = new OpenLayers.Handler.Click(this, {
            "click": this.mapDivClick
        }, {
            "single": true,
            "double": false,
            "stopSingle": true,
            "stopDouble": true,
            "pixelTolerance": 1,
            map: this.ovmap
        });
        this.handlers.click.activate();
        this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, null, true);
        this.rectEvents.register("mouseover", this, function (e) {
            if (!this.handlers.drag.active && !this.map.dragging) {
                this.handlers.drag.activate();
            }
        });
        this.rectEvents.register("mouseout", this, function (e) {
            if (!this.handlers.drag.dragging) {
                this.handlers.drag.deactivate();
            }
        });
        if (this.ovmap.getProjection() != this.map.getProjection()) {
            var sourceUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units;
            var targetUnits = this.ovmap.getProjectionObject().getUnits() || this.ovmap.units || this.ovmap.baseLayer.units;
            this.resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
        }
    },
    updateRectToMap: function () {
        var bounds;
        if (this.ovmap.getProjection() != this.map.getProjection()) {
            bounds = this.map.getExtent().transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject());
        } else {
            bounds = this.map.getExtent();
        }
        var pxBounds = this.getRectBoundsFromMapBounds(bounds);
        if (pxBounds) {
            this.setRectPxBounds(pxBounds);
        }
    },
    updateMapToRect: function () {
        var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds);
        if (this.ovmap.getProjection() != this.map.getProjection()) {
            lonLatBounds = lonLatBounds.transform(this.ovmap.getProjectionObject(), this.map.getProjectionObject());
        }
        this.map.panTo(lonLatBounds.getCenterLonLat());
    },
    setRectPxBounds: function (pxBounds) {
        var top = Math.max(pxBounds.top, 0);
        var left = Math.max(pxBounds.left, 0);
        var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), this.ovmap.size.h - this.hComp);
        var right = Math.min(pxBounds.left + pxBounds.getWidth(), this.ovmap.size.w - this.wComp);
        var width = Math.max(right - left, 0);
        var height = Math.max(bottom - top, 0);
        if (width < this.minRectSize || height < this.minRectSize) {
            this.extentRectangle.className = this.displayClass + this.minRectDisplayClass;
            var rLeft = left + (width / 2) - (this.minRectSize / 2);
            var rTop = top + (height / 2) - (this.minRectSize / 2);
            this.extentRectangle.style.top = Math.round(rTop) + 'px';
            this.extentRectangle.style.left = Math.round(rLeft) + 'px';
            this.extentRectangle.style.height = this.minRectSize + 'px';
            this.extentRectangle.style.width = this.minRectSize + 'px';
        } else {
            this.extentRectangle.className = this.displayClass + 'ExtentRectangle';
            this.extentRectangle.style.top = Math.round(top) + 'px';
            this.extentRectangle.style.left = Math.round(left) + 'px';
            this.extentRectangle.style.height = Math.round(height) + 'px';
            this.extentRectangle.style.width = Math.round(width) + 'px';
        }
        this.rectPxBounds = new OpenLayers.Bounds(Math.round(left), Math.round(bottom), Math.round(right), Math.round(top));
    },
    getRectBoundsFromMapBounds: function (lonLatBounds) {
        var leftBottomLonLat = new OpenLayers.LonLat(lonLatBounds.left, lonLatBounds.bottom);
        var rightTopLonLat = new OpenLayers.LonLat(lonLatBounds.right, lonLatBounds.top);
        var leftBottomPx = this.getOverviewPxFromLonLat(leftBottomLonLat);
        var rightTopPx = this.getOverviewPxFromLonLat(rightTopLonLat);
        var bounds = null;
        if (leftBottomPx && rightTopPx) {
            bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, rightTopPx.x, rightTopPx.y);
        }
        return bounds;
    },
    getMapBoundsFromRectBounds: function (pxBounds) {
        var leftBottomPx = new OpenLayers.Pixel(pxBounds.left, pxBounds.bottom);
        var rightTopPx = new OpenLayers.Pixel(pxBounds.right, pxBounds.top);
        var leftBottomLonLat = this.getLonLatFromOverviewPx(leftBottomPx);
        var rightTopLonLat = this.getLonLatFromOverviewPx(rightTopPx);
        return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, rightTopLonLat.lon, rightTopLonLat.lat);
    },
    getLonLatFromOverviewPx: function (overviewMapPx) {
        var size = this.ovmap.size;
        var res = this.ovmap.getResolution();
        var center = this.ovmap.getExtent().getCenterLonLat();
        var delta_x = overviewMapPx.x - (size.w / 2);
        var delta_y = overviewMapPx.y - (size.h / 2);
        return new OpenLayers.LonLat(center.lon + delta_x * res, center.lat - delta_y * res);
    },
    getOverviewPxFromLonLat: function (lonlat) {
        var res = this.ovmap.getResolution();
        var extent = this.ovmap.getExtent();
        var px = null;
        if (extent) {
            px = new OpenLayers.Pixel(Math.round(1 / res * (lonlat.lon - extent.left)), Math.round(1 / res * (extent.top - lonlat.lat)));
        }
        return px;
    },
    CLASS_NAME: 'OpenLayers.Control.OverviewMap'
});
OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
    MIN_ZOOM_LEVEL: 0,
    MAX_ZOOM_LEVEL: 21,
    RESOLUTIONS: [1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 0.0000858306884765625, 0.00004291534423828125, 0.00002145767211914062, 0.00001072883605957031, 0.00000536441802978515, 0.00000268220901489257, 0.0000013411045074462891, 0.00000067055225372314453],
    type: null,
    wrapDateLine: true,
    sphericalMercator: false,
    version: null,
    initialize: function (name, options) {
        options = options || {};
        if (!options.version) {
            options.version = typeof GMap2 === "function" ? "2" : "3";
        }
        var mixin = OpenLayers.Layer.Google["v" + options.version.replace(/\./g, "_")];
        if (mixin) {
            OpenLayers.Util.applyDefaults(options, mixin);
        } else {
            throw "Unsupported Google Maps API version: " + options.version;
        }
        OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS);
        if (options.maxExtent) {
            options.maxExtent = options.maxExtent.clone();
        }
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]);
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]);
        if (this.sphericalMercator) {
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
            this.initMercatorParameters();
        }
    },
    clone: function () {
        return new OpenLayers.Layer.Google(this.name, this.getOptions());
    },
    setVisibility: function (visible) {
        var opacity = this.opacity == null ? 1 : this.opacity;
        OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments);
        this.setOpacity(opacity);
    },
    display: function (visible) {
        if (!this._dragging) {
            this.setGMapVisibility(visible);
        }
        OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments);
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        this._dragging = dragging;
        OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments);
        delete this._dragging;
    },
    setOpacity: function (opacity) {
        if (opacity !== this.opacity) {
            if (this.map != null) {
                this.map.events.triggerEvent("changelayer", {
                    layer: this,
                    property: "opacity"
                });
            }
            this.opacity = opacity;
        }
        if (this.getVisibility()) {
            var container = this.getMapContainer();
            OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity);
        }
    },
    destroy: function () {
        if (this.map) {
            this.setGMapVisibility(false);
            var cache = OpenLayers.Layer.Google.cache[this.map.id];
            if (cache && cache.count <= 1) {
                this.removeGMapElements();
            }
        }
        OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments);
    },
    removeGMapElements: function () {
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            var container = this.mapObject && this.getMapContainer();
            if (container && container.parentNode) {
                container.parentNode.removeChild(container);
            }
            var termsOfUse = cache.termsOfUse;
            if (termsOfUse && termsOfUse.parentNode) {
                termsOfUse.parentNode.removeChild(termsOfUse);
            }
            var poweredBy = cache.poweredBy;
            if (poweredBy && poweredBy.parentNode) {
                poweredBy.parentNode.removeChild(poweredBy);
            }
        }
    },
    removeMap: function (map) {
        if (this.visibility && this.mapObject) {
            this.setGMapVisibility(false);
        }
        var cache = OpenLayers.Layer.Google.cache[map.id];
        if (cache) {
            if (cache.count <= 1) {
                this.removeGMapElements();
                delete OpenLayers.Layer.Google.cache[map.id];
            } else {
                --cache.count;
            }
        }
        delete this.termsOfUse;
        delete this.poweredBy;
        delete this.mapObject;
        delete this.dragObject;
        OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments);
    },
    getOLBoundsFromMapObjectBounds: function (moBounds) {
        var olBounds = null;
        if (moBounds != null) {
            var sw = moBounds.getSouthWest();
            var ne = moBounds.getNorthEast();
            if (this.sphericalMercator) {
                sw = this.forwardMercator(sw.lng(), sw.lat());
                ne = this.forwardMercator(ne.lng(), ne.lat());
            } else {
                sw = new OpenLayers.LonLat(sw.lng(), sw.lat());
                ne = new OpenLayers.LonLat(ne.lng(), ne.lat());
            }
            olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat);
        }
        return olBounds;
    },
    getWarningHTML: function () {
        return OpenLayers.i18n("googleWarning");
    },
    getMapObjectCenter: function () {
        return this.mapObject.getCenter();
    },
    getMapObjectZoom: function () {
        return this.mapObject.getZoom();
    },
    getLongitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng();
    },
    getLatitudeFromMapObjectLonLat: function (moLonLat) {
        var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat();
        return lat;
    },
    getXFromMapObjectPixel: function (moPixel) {
        return moPixel.x;
    },
    getYFromMapObjectPixel: function (moPixel) {
        return moPixel.y;
    },
    CLASS_NAME: "OpenLayers.Layer.Google"
});
OpenLayers.Layer.Google.cache = {};
OpenLayers.Layer.Google.v2 = {
    termsOfUse: null,
    poweredBy: null,
    dragObject: null,
    loadMapObject: function () {
        if (!this.type) {
            this.type = G_NORMAL_MAP;
        }
        var mapObject, termsOfUse, poweredBy;
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            mapObject = cache.mapObject;
            termsOfUse = cache.termsOfUse;
            poweredBy = cache.poweredBy;
            ++cache.count;
        } else {
            var container = this.map.viewPortDiv;
            var div = document.createElement("div");
            div.id = this.map.id + "_GMap2Container";
            div.style.position = "absolute";
            div.style.width = "100%";
            div.style.height = "100%";
            container.appendChild(div);
            try {
                mapObject = new GMap2(div);
                termsOfUse = div.lastChild;
                container.appendChild(termsOfUse);
                termsOfUse.style.zIndex = "1100";
                termsOfUse.style.right = "";
                termsOfUse.style.bottom = "";
                termsOfUse.className = "olLayerGoogleCopyright";
                poweredBy = div.lastChild;
                container.appendChild(poweredBy);
                poweredBy.style.zIndex = "1100";
                poweredBy.style.right = "";
                poweredBy.style.bottom = "";
                poweredBy.className = "olLayerGooglePoweredBy gmnoprint";
            } catch (e) {
                throw (e);
            }
            OpenLayers.Layer.Google.cache[this.map.id] = {
                mapObject: mapObject,
                termsOfUse: termsOfUse,
                poweredBy: poweredBy,
                count: 1
            };
        }
        this.mapObject = mapObject;
        this.termsOfUse = termsOfUse;
        this.poweredBy = poweredBy;
        if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) {
            this.mapObject.addMapType(this.type);
        }
        if (typeof mapObject.getDragObject == "function") {
            this.dragObject = mapObject.getDragObject();
        } else {
            this.dragPanMapObject = null;
        }
        if (this.isBaseLayer === false) {
            this.setGMapVisibility(this.div.style.display !== "none");
        }
    },
    onMapResize: function () {
        if (this.visibility && this.mapObject.isLoaded()) {
            this.mapObject.checkResize();
        } else {
            if (!this._resized) {
                var layer = this;
                var handle = GEvent.addListener(this.mapObject, "load", function () {
                    GEvent.removeListener(handle);
                    delete layer._resized;
                    layer.mapObject.checkResize();
                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
                });
            }
            this._resized = true;
        }
    },
    setGMapVisibility: function (visible) {
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            var container = this.mapObject.getContainer();
            if (visible === true) {
                this.mapObject.setMapType(this.type);
                container.style.display = "";
                this.termsOfUse.style.left = "";
                this.termsOfUse.style.display = "";
                this.poweredBy.style.display = "";
                cache.displayed = this.id;
            } else {
                if (cache.displayed === this.id) {
                    delete cache.displayed;
                }
                if (!cache.displayed) {
                    container.style.display = "none";
                    this.termsOfUse.style.display = "none";
                    this.termsOfUse.style.left = "-9999px";
                    this.poweredBy.style.display = "none";
                }
            }
        }
    },
    getMapContainer: function () {
        return this.mapObject.getContainer();
    },
    getMapObjectBoundsFromOLBounds: function (olBounds) {
        var moBounds = null;
        if (olBounds != null) {
            var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
            var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);
            moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon));
        }
        return moBounds;
    },
    setMapObjectCenter: function (center, zoom) {
        this.mapObject.setCenter(center, zoom);
    },
    dragPanMapObject: function (dX, dY) {
        this.dragObject.moveBy(new GSize(-dX, dY));
    },
    getMapObjectLonLatFromMapObjectPixel: function (moPixel) {
        return this.mapObject.fromContainerPixelToLatLng(moPixel);
    },
    getMapObjectPixelFromMapObjectLonLat: function (moLonLat) {
        return this.mapObject.fromLatLngToContainerPixel(moLonLat);
    },
    getMapObjectZoomFromMapObjectBounds: function (moBounds) {
        return this.mapObject.getBoundsZoomLevel(moBounds);
    },
    getMapObjectLonLatFromLonLat: function (lon, lat) {
        var gLatLng;
        if (this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            gLatLng = new GLatLng(lonlat.lat, lonlat.lon);
        } else {
            gLatLng = new GLatLng(lat, lon);
        }
        return gLatLng;
    },
    getMapObjectPixelFromXY: function (x, y) {
        return new GPoint(x, y);
    }
};
OpenLayers.Format.XML = OpenLayers.Class(OpenLayers.Format, {
    namespaces: null,
    namespaceAlias: null,
    defaultPrefix: null,
    readers: {},
    writers: {},
    xmldom: null,
    initialize: function (options) {
        if (window.ActiveXObject) {
            this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
        }
        OpenLayers.Format.prototype.initialize.apply(this, [options]);
        this.namespaces = OpenLayers.Util.extend({}, this.namespaces);
        this.namespaceAlias = {};
        for (var alias in this.namespaces) {
            this.namespaceAlias[this.namespaces[alias]] = alias;
        }
    },
    destroy: function () {
        this.xmldom = null;
        OpenLayers.Format.prototype.destroy.apply(this, arguments);
    },
    setNamespace: function (alias, uri) {
        this.namespaces[alias] = uri;
        this.namespaceAlias[uri] = alias;
    },
    read: function (text) {
        var index = text.indexOf('<');
        if (index > 0) {
            text = text.substring(index);
        }
        var node = OpenLayers.Util.Try(OpenLayers.Function.bind((function () {
            var xmldom;
            if (window.ActiveXObject && !this.xmldom) {
                xmldom = new ActiveXObject("Microsoft.XMLDOM");
            } else {
                xmldom = this.xmldom;
            }
            xmldom.loadXML(text);
            return xmldom;
        }), this), function () {
            return new DOMParser().parseFromString(text, 'text/xml');
        }, function () {
            var req = new XMLHttpRequest();
            req.open("GET", "data:" + "text/xml" + ";charset=utf-8," + encodeURIComponent(text), false);
            if (req.overrideMimeType) {
                req.overrideMimeType("text/xml");
            }
            req.send(null);
            return req.responseXML;
        });
        if (this.keepData) {
            this.data = node;
        }
        return node;
    },
    write: function (node) {
        var data;
        if (this.xmldom) {
            data = node.xml;
        } else {
            var serializer = new XMLSerializer();
            if (node.nodeType == 1) {
                var doc = document.implementation.createDocument("", "", null);
                if (doc.importNode) {
                    node = doc.importNode(node, true);
                }
                doc.appendChild(node);
                data = serializer.serializeToString(doc);
            } else {
                data = serializer.serializeToString(node);
            }
        }
        return data;
    },
    createElementNS: function (uri, name) {
        var element;
        if (this.xmldom) {
            if (typeof uri == "string") {
                element = this.xmldom.createNode(1, name, uri);
            } else {
                element = this.xmldom.createNode(1, name, "");
            }
        } else {
            element = document.createElementNS(uri, name);
        }
        return element;
    },
    createTextNode: function (text) {
        var node;
        if (typeof text !== "string") {
            text = String(text);
        }
        if (this.xmldom) {
            node = this.xmldom.createTextNode(text);
        } else {
            node = document.createTextNode(text);
        }
        return node;
    },
    getElementsByTagNameNS: function (node, uri, name) {
        var elements = [];
        if (node.getElementsByTagNameNS) {
            elements = node.getElementsByTagNameNS(uri, name);
        } else {
            var allNodes = node.getElementsByTagName("*");
            var potentialNode, fullName;
            for (var i = 0, len = allNodes.length; i < len; ++i) {
                potentialNode = allNodes[i];
                fullName = (potentialNode.prefix) ? (potentialNode.prefix + ":" + name) : name;
                if ((name == "*") || (fullName == potentialNode.nodeName)) {
                    if ((uri == "*") || (uri == potentialNode.namespaceURI)) {
                        elements.push(potentialNode);
                    }
                }
            }
        }
        return elements;
    },
    getAttributeNodeNS: function (node, uri, name) {
        var attributeNode = null;
        if (node.getAttributeNodeNS) {
            attributeNode = node.getAttributeNodeNS(uri, name);
        } else {
            var attributes = node.attributes;
            var potentialNode, fullName;
            for (var i = 0, len = attributes.length; i < len; ++i) {
                potentialNode = attributes[i];
                if (potentialNode.namespaceURI == uri) {
                    fullName = (potentialNode.prefix) ? (potentialNode.prefix + ":" + name) : name;
                    if (fullName == potentialNode.nodeName) {
                        attributeNode = potentialNode;
                        break;
                    }
                }
            }
        }
        return attributeNode;
    },
    getAttributeNS: function (node, uri, name) {
        var attributeValue = "";
        if (node.getAttributeNS) {
            attributeValue = node.getAttributeNS(uri, name) || "";
        } else {
            var attributeNode = this.getAttributeNodeNS(node, uri, name);
            if (attributeNode) {
                attributeValue = attributeNode.nodeValue;
            }
        }
        return attributeValue;
    },
    getChildValue: function (node, def) {
        var value = def || "";
        if (node) {
            for (var child = node.firstChild; child; child = child.nextSibling) {
                switch (child.nodeType) {
                case 3:
                case 4:
                    value += child.nodeValue;
                }
            }
        }
        return value;
    },
    concatChildValues: function (node, def) {
        var value = "";
        var child = node.firstChild;
        var childValue;
        while (child) {
            childValue = child.nodeValue;
            if (childValue) {
                value += childValue;
            }
            child = child.nextSibling;
        }
        if (value == "" && def != undefined) {
            value = def;
        }
        return value;
    },
    isSimpleContent: function (node) {
        var simple = true;
        for (var child = node.firstChild; child; child = child.nextSibling) {
            if (child.nodeType === 1) {
                simple = false;
                break;
            }
        }
        return simple;
    },
    contentType: function (node) {
        var simple = false,
            complex = false;
        var type = OpenLayers.Format.XML.CONTENT_TYPE.EMPTY;
        for (var child = node.firstChild; child; child = child.nextSibling) {
            switch (child.nodeType) {
            case 1:
                complex = true;
                break;
            case 8:
                break;
            default:
                simple = true;
            }
            if (complex && simple) {
                break;
            }
        }
        if (complex && simple) {
            type = OpenLayers.Format.XML.CONTENT_TYPE.MIXED;
        } else if (complex) {
            return OpenLayers.Format.XML.CONTENT_TYPE.COMPLEX;
        } else if (simple) {
            return OpenLayers.Format.XML.CONTENT_TYPE.SIMPLE;
        }
        return type;
    },
    hasAttributeNS: function (node, uri, name) {
        var found = false;
        if (node.hasAttributeNS) {
            found = node.hasAttributeNS(uri, name);
        } else {
            found = !! this.getAttributeNodeNS(node, uri, name);
        }
        return found;
    },
    setAttributeNS: function (node, uri, name, value) {
        if (node.setAttributeNS) {
            node.setAttributeNS(uri, name, value);
        } else {
            if (this.xmldom) {
                if (uri) {
                    var attribute = node.ownerDocument.createNode(2, name, uri);
                    attribute.nodeValue = value;
                    node.setAttributeNode(attribute);
                } else {
                    node.setAttribute(name, value);
                }
            } else {
                throw "setAttributeNS not implemented";
            }
        }
    },
    createElementNSPlus: function (name, options) {
        options = options || {};
        var uri = options.uri || this.namespaces[options.prefix];
        if (!uri) {
            var loc = name.indexOf(":");
            uri = this.namespaces[name.substring(0, loc)];
        }
        if (!uri) {
            uri = this.namespaces[this.defaultPrefix];
        }
        var node = this.createElementNS(uri, name);
        if (options.attributes) {
            this.setAttributes(node, options.attributes);
        }
        var value = options.value;
        if (value != null) {
            node.appendChild(this.createTextNode(value));
        }
        return node;
    },
    setAttributes: function (node, obj) {
        var value, uri;
        for (var name in obj) {
            if (obj[name] != null && obj[name].toString) {
                value = obj[name].toString();
                uri = this.namespaces[name.substring(0, name.indexOf(":"))] || null;
                this.setAttributeNS(node, uri, name, value);
            }
        }
    },
    readNode: function (node, obj) {
        if (!obj) {
            obj = {};
        }
        var group = this.readers[node.namespaceURI ? this.namespaceAlias[node.namespaceURI] : this.defaultPrefix];
        if (group) {
            var local = node.localName || node.nodeName.split(":").pop();
            var reader = group[local] || group["*"];
            if (reader) {
                reader.apply(this, [node, obj]);
            }
        }
        return obj;
    },
    readChildNodes: function (node, obj) {
        if (!obj) {
            obj = {};
        }
        var children = node.childNodes;
        var child;
        for (var i = 0, len = children.length; i < len; ++i) {
            child = children[i];
            if (child.nodeType == 1) {
                this.readNode(child, obj);
            }
        }
        return obj;
    },
    writeNode: function (name, obj, parent) {
        var prefix, local;
        var split = name.indexOf(":");
        if (split > 0) {
            prefix = name.substring(0, split);
            local = name.substring(split + 1);
        } else {
            if (parent) {
                prefix = this.namespaceAlias[parent.namespaceURI];
            } else {
                prefix = this.defaultPrefix;
            }
            local = name;
        }
        var child = this.writers[prefix][local].apply(this, [obj]);
        if (parent) {
            parent.appendChild(child);
        }
        return child;
    },
    getChildEl: function (node, name, uri) {
        return node && this.getThisOrNextEl(node.firstChild, name, uri);
    },
    getNextEl: function (node, name, uri) {
        return node && this.getThisOrNextEl(node.nextSibling, name, uri);
    },
    getThisOrNextEl: function (node, name, uri) {
        outer: for (var sibling = node; sibling; sibling = sibling.nextSibling) {
            switch (sibling.nodeType) {
            case 1:
                if ((!name || name === (sibling.localName || sibling.nodeName.split(":").pop())) && (!uri || uri === sibling.namespaceURI)) {
                    break outer;
                }
                sibling = null;
                break outer;
            case 3:
                if (/^\s*$/.test(sibling.nodeValue)) {
                    break;
                }
            case 4:
            case 6:
            case 12:
            case 10:
            case 11:
                sibling = null;
                break outer;
            }
        }
        return sibling || null;
    },
    lookupNamespaceURI: function (node, prefix) {
        var uri = null;
        if (node) {
            if (node.lookupNamespaceURI) {
                uri = node.lookupNamespaceURI(prefix);
            } else {
                outer: switch (node.nodeType) {
                case 1:
                    if (node.namespaceURI !== null && node.prefix === prefix) {
                        uri = node.namespaceURI;
                        break outer;
                    }
                    var len = node.attributes.length;
                    if (len) {
                        var attr;
                        for (var i = 0; i < len; ++i) {
                            attr = node.attributes[i];
                            if (attr.prefix === "xmlns" && attr.name === "xmlns:" + prefix) {
                                uri = attr.value || null;
                                break outer;
                            } else if (attr.name === "xmlns" && prefix === null) {
                                uri = attr.value || null;
                                break outer;
                            }
                        }
                    }
                    uri = this.lookupNamespaceURI(node.parentNode, prefix);
                    break outer;
                case 2:
                    uri = this.lookupNamespaceURI(node.ownerElement, prefix);
                    break outer;
                case 9:
                    uri = this.lookupNamespaceURI(node.documentElement, prefix);
                    break outer;
                case 6:
                case 12:
                case 10:
                case 11:
                    break outer;
                default:
                    uri = this.lookupNamespaceURI(node.parentNode, prefix);
                    break outer;
                }
            }
        }
        return uri;
    },
    getXMLDoc: function () {
        if (!OpenLayers.Format.XML.document && !this.xmldom) {
            if (document.implementation && document.implementation.createDocument) {
                OpenLayers.Format.XML.document = document.implementation.createDocument("", "", null);
            } else if (!this.xmldom && window.ActiveXObject) {
                this.xmldom = new ActiveXObject("Microsoft.XMLDOM");
            }
        }
        return OpenLayers.Format.XML.document || this.xmldom;
    },
    CLASS_NAME: "OpenLayers.Format.XML"
});
OpenLayers.Format.XML.CONTENT_TYPE = {
    EMPTY: 0,
    SIMPLE: 1,
    COMPLEX: 2,
    MIXED: 3
};
OpenLayers.Format.XML.lookupNamespaceURI = OpenLayers.Function.bind(OpenLayers.Format.XML.prototype.lookupNamespaceURI, OpenLayers.Format.XML.prototype);
OpenLayers.Format.XML.document = null;
OpenLayers.Format.WFST = function (options) {
    options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.WFST.DEFAULTS);
    var cls = OpenLayers.Format.WFST["v" + options.version.replace(/\./g, "_")];
    if (!cls) {
        throw "Unsupported WFST version: " + options.version;
    }
    return new cls(options);
};
OpenLayers.Format.WFST.DEFAULTS = {
    "version": "1.0.0"
};
OpenLayers.Format.WFST.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
        wfs: "http://www.opengis.net/wfs",
        gml: "http://www.opengis.net/gml",
        ogc: "http://www.opengis.net/ogc",
        ows: "http://www.opengis.net/ows"
    },
    defaultPrefix: "wfs",
    version: null,
    schemaLocations: null,
    srsName: null,
    extractAttributes: true,
    xy: true,
    stateName: null,
    initialize: function (options) {
        this.stateName = {};
        this.stateName[OpenLayers.State.INSERT] = "wfs:Insert";
        this.stateName[OpenLayers.State.UPDATE] = "wfs:Update";
        this.stateName[OpenLayers.State.DELETE] = "wfs:Delete";
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    getSrsName: function (feature, options) {
        var srsName = options && options.srsName;
        if (!srsName) {
            if (feature && feature.layer) {
                srsName = feature.layer.projection.getCode();
            } else {
                srsName = this.srsName;
            }
        }
        return srsName;
    },
    read: function (data, options) {
        options = options || {};
        OpenLayers.Util.applyDefaults(options, {
            output: "features"
        });
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var obj = {};
        if (data) {
            this.readNode(data, obj, true);
        }
        if (obj.features && options.output === "features") {
            obj = obj.features;
        }
        return obj;
    },
    readers: {
        "wfs": {
            "FeatureCollection": function (node, obj) {
                obj.features = [];
                this.readChildNodes(node, obj);
            }
        }
    },
    write: function (features, options) {
        var node = this.writeNode("wfs:Transaction", {
            features: features,
            options: options
        });
        var value = this.schemaLocationAttr();
        if (value) {
            this.setAttributeNS(node, this.namespaces["xsi"], "xsi:schemaLocation", value);
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    writers: {
        "wfs": {
            "GetFeature": function (options) {
                var node = this.createElementNSPlus("wfs:GetFeature", {
                    attributes: {
                        service: "WFS",
                        version: this.version,
                        handle: options && options.handle,
                        outputFormat: options && options.outputFormat,
                        maxFeatures: options && options.maxFeatures,
                        "xsi:schemaLocation": this.schemaLocationAttr(options)
                    }
                });
                if (typeof this.featureType == "string") {
                    this.writeNode("Query", options, node);
                } else {
                    for (var i = 0, len = this.featureType.length; i < len; i++) {
                        options.featureType = this.featureType[i];
                        this.writeNode("Query", options, node);
                    }
                }
                return node;
            },
            "Transaction": function (obj) {
                obj = obj || {};
                var options = obj.options || {};
                var node = this.createElementNSPlus("wfs:Transaction", {
                    attributes: {
                        service: "WFS",
                        version: this.version,
                        handle: options.handle
                    }
                });
                var i, len;
                var features = obj.features;
                if (features) {
                    if (options.multi === true) {
                        OpenLayers.Util.extend(this.geometryTypes, {
                            "OpenLayers.Geometry.Point": "MultiPoint",
                            "OpenLayers.Geometry.LineString": (this.multiCurve === true) ? "MultiCurve" : "MultiLineString",
                            "OpenLayers.Geometry.Polygon": (this.multiSurface === true) ? "MultiSurface" : "MultiPolygon"
                        });
                    }
                    var name, feature;
                    for (i = 0, len = features.length; i < len; ++i) {
                        feature = features[i];
                        name = this.stateName[feature.state];
                        if (name) {
                            this.writeNode(name, {
                                feature: feature,
                                options: options
                            }, node);
                        }
                    }
                    if (options.multi === true) {
                        this.setGeometryTypes();
                    }
                }
                if (options.nativeElements) {
                    for (i = 0, len = options.nativeElements.length; i < len; ++i) {
                        this.writeNode("wfs:Native", options.nativeElements[i], node);
                    }
                }
                return node;
            },
            "Native": function (nativeElement) {
                var node = this.createElementNSPlus("wfs:Native", {
                    attributes: {
                        vendorId: nativeElement.vendorId,
                        safeToIgnore: nativeElement.safeToIgnore
                    },
                    value: nativeElement.value
                });
                return node;
            },
            "Insert": function (obj) {
                var feature = obj.feature;
                var options = obj.options;
                var node = this.createElementNSPlus("wfs:Insert", {
                    attributes: {
                        handle: options && options.handle
                    }
                });
                this.srsName = this.getSrsName(feature);
                this.writeNode("feature:_typeName", feature, node);
                return node;
            },
            "Update": function (obj) {
                var feature = obj.feature;
                var options = obj.options;
                var node = this.createElementNSPlus("wfs:Update", {
                    attributes: {
                        handle: options && options.handle,
                        typeName: (this.featureNS ? this.featurePrefix + ":" : "") + this.featureType
                    }
                });
                if (this.featureNS) {
                    node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
                }
                var modified = feature.modified;
                if (this.geometryName !== null && (!modified || modified.geometry !== undefined)) {
                    this.srsName = this.getSrsName(feature);
                    this.writeNode("Property", {
                        name: this.geometryName,
                        value: feature.geometry
                    }, node);
                }
                for (var key in feature.attributes) {
                    if (feature.attributes[key] !== undefined && (!modified || !modified.attributes || (modified.attributes && modified.attributes[key] !== undefined))) {
                        this.writeNode("Property", {
                            name: key,
                            value: feature.attributes[key]
                        }, node);
                    }
                }
                this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
                    fids: [feature.fid]
                }), node);
                return node;
            },
            "Property": function (obj) {
                var node = this.createElementNSPlus("wfs:Property");
                this.writeNode("Name", obj.name, node);
                if (obj.value !== null) {
                    this.writeNode("Value", obj.value, node);
                }
                return node;
            },
            "Name": function (name) {
                return this.createElementNSPlus("wfs:Name", {
                    value: name
                });
            },
            "Value": function (obj) {
                var node;
                if (obj instanceof OpenLayers.Geometry) {
                    node = this.createElementNSPlus("wfs:Value");
                    var geom = this.writeNode("feature:_geometry", obj).firstChild;
                    node.appendChild(geom);
                } else {
                    node = this.createElementNSPlus("wfs:Value", {
                        value: obj
                    });
                }
                return node;
            },
            "Delete": function (obj) {
                var feature = obj.feature;
                var options = obj.options;
                var node = this.createElementNSPlus("wfs:Delete", {
                    attributes: {
                        handle: options && options.handle,
                        typeName: (this.featureNS ? this.featurePrefix + ":" : "") + this.featureType
                    }
                });
                if (this.featureNS) {
                    node.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
                }
                this.writeNode("ogc:Filter", new OpenLayers.Filter.FeatureId({
                    fids: [feature.fid]
                }), node);
                return node;
            }
        }
    },
    schemaLocationAttr: function (options) {
        options = OpenLayers.Util.extend({
            featurePrefix: this.featurePrefix,
            schema: this.schema
        }, options);
        var schemaLocations = OpenLayers.Util.extend({}, this.schemaLocations);
        if (options.schema) {
            schemaLocations[options.featurePrefix] = options.schema;
        }
        var parts = [];
        var uri;
        for (var key in schemaLocations) {
            uri = this.namespaces[key];
            if (uri) {
                parts.push(uri + " " + schemaLocations[key]);
            }
        }
        var value = parts.join(" ") || undefined;
        return value;
    },
    setFilterProperty: function (filter) {
        if (filter.filters) {
            for (var i = 0, len = filter.filters.length; i < len; ++i) {
                this.setFilterProperty(filter.filters[i]);
            }
        } else {
            if (filter instanceof OpenLayers.Filter.Spatial) {
                filter.property = this.geometryName;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.WFST.v1"
});
OpenLayers.Format.OGCExceptionReport = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ogc: "http://www.opengis.net/ogc"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    defaultPrefix: "ogc",
    read: function (data) {
        var result;
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var root = data.documentElement;
        var exceptionInfo = {
            exceptionReport: null
        };
        if (root) {
            this.readChildNodes(data, exceptionInfo);
            if (exceptionInfo.exceptionReport === null) {
                exceptionInfo = new OpenLayers.Format.OWSCommon().read(data);
            }
        }
        return exceptionInfo;
    },
    readers: {
        "ogc": {
            "ServiceExceptionReport": function (node, obj) {
                obj.exceptionReport = {
                    exceptions: []
                };
                this.readChildNodes(node, obj.exceptionReport);
            },
            "ServiceException": function (node, exceptionReport) {
                var exception = {
                    code: node.getAttribute("code"),
                    locator: node.getAttribute("locator"),
                    text: this.getChildValue(node)
                };
                exceptionReport.exceptions.push(exception);
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.OGCExceptionReport"
});
OpenLayers.Format.XML.VersionedOGC = OpenLayers.Class(OpenLayers.Format.XML, {
    defaultVersion: null,
    version: null,
    profile: null,
    errorProperty: null,
    name: null,
    stringifyOutput: false,
    parser: null,
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        var className = this.CLASS_NAME;
        this.name = className.substring(className.lastIndexOf(".") + 1);
    },
    getVersion: function (root, options) {
        var version;
        if (root) {
            version = this.version;
            if (!version) {
                version = root.getAttribute("version");
                if (!version) {
                    version = this.defaultVersion;
                }
            }
        } else {
            version = (options && options.version) || this.version || this.defaultVersion;
        }
        return version;
    },
    getParser: function (version) {
        version = version || this.defaultVersion;
        var profile = this.profile ? "_" + this.profile : "";
        if (!this.parser || this.parser.VERSION != version) {
            var format = OpenLayers.Format[this.name]["v" + version.replace(/\./g, "_") + profile];
            if (!format) {
                throw "Can't find a " + this.name + " parser for version " + version + profile;
            }
            this.parser = new format(this.options);
        }
        return this.parser;
    },
    write: function (obj, options) {
        var version = this.getVersion(null, options);
        this.parser = this.getParser(version);
        var root = this.parser.write(obj, options);
        if (this.stringifyOutput === false) {
            return root;
        } else {
            return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
        }
    },
    read: function (data, options) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var root = data.documentElement;
        var version = this.getVersion(root);
        this.parser = this.getParser(version);
        var obj = this.parser.read(data, options);
        if (this.errorProperty !== null && obj[this.errorProperty] === undefined) {
            var format = new OpenLayers.Format.OGCExceptionReport();
            obj.error = format.read(data);
        }
        obj.version = version;
        return obj;
    },
    CLASS_NAME: "OpenLayers.Format.XML.VersionedOGC"
});
OpenLayers.Style = OpenLayers.Class({
    id: null,
    name: null,
    title: null,
    description: null,
    layerName: null,
    isDefault: false,
    rules: null,
    context: null,
    defaultStyle: null,
    defaultsPerSymbolizer: false,
    propertyStyles: null,
    initialize: function (style, options) {
        OpenLayers.Util.extend(this, options);
        this.rules = [];
        if (options && options.rules) {
            this.addRules(options.rules);
        }
        this.setDefaultStyle(style || OpenLayers.Feature.Vector.style["default"]);
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    destroy: function () {
        for (var i = 0, len = this.rules.length; i < len; i++) {
            this.rules[i].destroy();
            this.rules[i] = null;
        }
        this.rules = null;
        this.defaultStyle = null;
    },
    createSymbolizer: function (feature) {
        var style = this.defaultsPerSymbolizer ? {} : this.createLiterals(OpenLayers.Util.extend({}, this.defaultStyle), feature);
        var rules = this.rules;
        var rule, context;
        var elseRules = [];
        var appliedRules = false;
        for (var i = 0, len = rules.length; i < len; i++) {
            rule = rules[i];
            var applies = rule.evaluate(feature);
            if (applies) {
                if (rule instanceof OpenLayers.Rule && rule.elseFilter) {
                    elseRules.push(rule);
                } else {
                    appliedRules = true;
                    this.applySymbolizer(rule, style, feature);
                }
            }
        }
        if (appliedRules == false && elseRules.length > 0) {
            appliedRules = true;
            for (var i = 0, len = elseRules.length; i < len; i++) {
                this.applySymbolizer(elseRules[i], style, feature);
            }
        }
        if (rules.length > 0 && appliedRules == false) {
            style.display = "none";
        }
        if (style.label && typeof style.label !== "string") {
            style.label = String(style.label);
        }
        return style;
    },
    applySymbolizer: function (rule, style, feature) {
        var symbolizerPrefix = feature.geometry ? this.getSymbolizerPrefix(feature.geometry) : OpenLayers.Style.SYMBOLIZER_PREFIXES[0];
        var symbolizer = rule.symbolizer[symbolizerPrefix] || rule.symbolizer;
        if (this.defaultsPerSymbolizer === true) {
            var defaults = this.defaultStyle;
            OpenLayers.Util.applyDefaults(symbolizer, {
                pointRadius: defaults.pointRadius
            });
            if (symbolizer.stroke === true || symbolizer.graphic === true) {
                OpenLayers.Util.applyDefaults(symbolizer, {
                    strokeWidth: defaults.strokeWidth,
                    strokeColor: defaults.strokeColor,
                    strokeOpacity: defaults.strokeOpacity,
                    strokeDashstyle: defaults.strokeDashstyle,
                    strokeLinecap: defaults.strokeLinecap
                });
            }
            if (symbolizer.fill === true || symbolizer.graphic === true) {
                OpenLayers.Util.applyDefaults(symbolizer, {
                    fillColor: defaults.fillColor,
                    fillOpacity: defaults.fillOpacity
                });
            }
            if (symbolizer.graphic === true) {
                OpenLayers.Util.applyDefaults(symbolizer, {
                    pointRadius: this.defaultStyle.pointRadius,
                    externalGraphic: this.defaultStyle.externalGraphic,
                    graphicName: this.defaultStyle.graphicName,
                    graphicOpacity: this.defaultStyle.graphicOpacity,
                    graphicWidth: this.defaultStyle.graphicWidth,
                    graphicHeight: this.defaultStyle.graphicHeight,
                    graphicXOffset: this.defaultStyle.graphicXOffset,
                    graphicYOffset: this.defaultStyle.graphicYOffset
                });
            }
        }
        return this.createLiterals(OpenLayers.Util.extend(style, symbolizer), feature);
    },
    createLiterals: function (style, feature) {
        var context = OpenLayers.Util.extend({}, feature.attributes || feature.data);
        OpenLayers.Util.extend(context, this.context);
        for (var i in this.propertyStyles) {
            style[i] = OpenLayers.Style.createLiteral(style[i], context, feature, i);
        }
        return style;
    },
    findPropertyStyles: function () {
        var propertyStyles = {};
        var style = this.defaultStyle;
        this.addPropertyStyles(propertyStyles, style);
        var rules = this.rules;
        var symbolizer, value;
        for (var i = 0, len = rules.length; i < len; i++) {
            symbolizer = rules[i].symbolizer;
            for (var key in symbolizer) {
                value = symbolizer[key];
                if (typeof value == "object") {
                    this.addPropertyStyles(propertyStyles, value);
                } else {
                    this.addPropertyStyles(propertyStyles, symbolizer);
                    break;
                }
            }
        }
        return propertyStyles;
    },
    addPropertyStyles: function (propertyStyles, symbolizer) {
        var property;
        for (var key in symbolizer) {
            property = symbolizer[key];
            if (typeof property == "string" && property.match(/\$\{\w+\}/)) {
                propertyStyles[key] = true;
            }
        }
        return propertyStyles;
    },
    addRules: function (rules) {
        Array.prototype.push.apply(this.rules, rules);
        this.propertyStyles = this.findPropertyStyles();
    },
    setDefaultStyle: function (style) {
        this.defaultStyle = style;
        this.propertyStyles = this.findPropertyStyles();
    },
    getSymbolizerPrefix: function (geometry) {
        var prefixes = OpenLayers.Style.SYMBOLIZER_PREFIXES;
        for (var i = 0, len = prefixes.length; i < len; i++) {
            if (geometry.CLASS_NAME.indexOf(prefixes[i]) != -1) {
                return prefixes[i];
            }
        }
    },
    clone: function () {
        var options = OpenLayers.Util.extend({}, this);
        if (this.rules) {
            options.rules = [];
            for (var i = 0, len = this.rules.length; i < len; ++i) {
                options.rules.push(this.rules[i].clone());
            }
        }
        options.context = this.context && OpenLayers.Util.extend({}, this.context);
        var defaultStyle = OpenLayers.Util.extend({}, this.defaultStyle);
        return new OpenLayers.Style(defaultStyle, options);
    },
    CLASS_NAME: "OpenLayers.Style"
});
OpenLayers.Style.createLiteral = function (value, context, feature, property) {
    if (typeof value == "string" && value.indexOf("${") != -1) {
        value = OpenLayers.String.format(value, context, [feature, property]);
        value = (isNaN(value) || !value) ? value : parseFloat(value);
    }
    return value;
};
OpenLayers.Style.SYMBOLIZER_PREFIXES = ['Point', 'Line', 'Polygon', 'Text', 'Raster'];
OpenLayers.Filter = OpenLayers.Class({
    initialize: function (options) {
        OpenLayers.Util.extend(this, options);
    },
    destroy: function () {},
    evaluate: function (context) {
        return true;
    },
    clone: function () {
        return null;
    },
    CLASS_NAME: "OpenLayers.Filter"
});
OpenLayers.Filter.FeatureId = OpenLayers.Class(OpenLayers.Filter, {
    fids: null,
    type: "FID",
    initialize: function (options) {
        this.fids = [];
        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
    },
    evaluate: function (feature) {
        for (var i = 0, len = this.fids.length; i < len; i++) {
            var fid = feature.fid || feature.id;
            if (fid == this.fids[i]) {
                return true;
            }
        }
        return false;
    },
    clone: function () {
        var filter = new OpenLayers.Filter.FeatureId();
        OpenLayers.Util.extend(filter, this);
        filter.fids = this.fids.slice();
        return filter;
    },
    CLASS_NAME: "OpenLayers.Filter.FeatureId"
});
OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, {
    filters: null,
    type: null,
    initialize: function (options) {
        this.filters = [];
        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
    },
    destroy: function () {
        this.filters = null;
        OpenLayers.Filter.prototype.destroy.apply(this);
    },
    evaluate: function (context) {
        var i, len;
        switch (this.type) {
        case OpenLayers.Filter.Logical.AND:
            for (i = 0, len = this.filters.length; i < len; i++) {
                if (this.filters[i].evaluate(context) == false) {
                    return false;
                }
            }
            return true;
        case OpenLayers.Filter.Logical.OR:
            for (i = 0, len = this.filters.length; i < len; i++) {
                if (this.filters[i].evaluate(context) == true) {
                    return true;
                }
            }
            return false;
        case OpenLayers.Filter.Logical.NOT:
            return (!this.filters[0].evaluate(context));
        }
        return undefined;
    },
    clone: function () {
        var filters = [];
        for (var i = 0, len = this.filters.length; i < len; ++i) {
            filters.push(this.filters[i].clone());
        }
        return new OpenLayers.Filter.Logical({
            type: this.type,
            filters: filters
        });
    },
    CLASS_NAME: "OpenLayers.Filter.Logical"
});
OpenLayers.Filter.Logical.AND = "&&";
OpenLayers.Filter.Logical.OR = "||";
OpenLayers.Filter.Logical.NOT = "!";
OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, {
    type: null,
    property: null,
    value: null,
    matchCase: true,
    lowerBoundary: null,
    upperBoundary: null,
    initialize: function (options) {
        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
        if (this.type === OpenLayers.Filter.Comparison.LIKE && options.matchCase === undefined) {
            this.matchCase = null;
        }
    },
    evaluate: function (context) {
        if (context instanceof OpenLayers.Feature.Vector) {
            context = context.attributes;
        }
        var result = false;
        var got = context[this.property];
        var exp;
        switch (this.type) {
        case OpenLayers.Filter.Comparison.EQUAL_TO:
            exp = this.value;
            if (!this.matchCase && typeof got == "string" && typeof exp == "string") {
                result = (got.toUpperCase() == exp.toUpperCase());
            } else {
                result = (got == exp);
            }
            break;
        case OpenLayers.Filter.Comparison.NOT_EQUAL_TO:
            exp = this.value;
            if (!this.matchCase && typeof got == "string" && typeof exp == "string") {
                result = (got.toUpperCase() != exp.toUpperCase());
            } else {
                result = (got != exp);
            }
            break;
        case OpenLayers.Filter.Comparison.LESS_THAN:
            result = got < this.value;
            break;
        case OpenLayers.Filter.Comparison.GREATER_THAN:
            result = got > this.value;
            break;
        case OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO:
            result = got <= this.value;
            break;
        case OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO:
            result = got >= this.value;
            break;
        case OpenLayers.Filter.Comparison.BETWEEN:
            result = (got >= this.lowerBoundary) && (got <= this.upperBoundary);
            break;
        case OpenLayers.Filter.Comparison.LIKE:
            var regexp = new RegExp(this.value, "gi");
            result = regexp.test(got);
            break;
        }
        return result;
    },
    value2regex: function (wildCard, singleChar, escapeChar) {
        if (wildCard == ".") {
            var msg = "'.' is an unsupported wildCard character for " + "OpenLayers.Filter.Comparison";
            OpenLayers.Console.error(msg);
            return null;
        }
        wildCard = wildCard ? wildCard : "*";
        singleChar = singleChar ? singleChar : ".";
        escapeChar = escapeChar ? escapeChar : "!";
        this.value = this.value.replace(new RegExp("\\" + escapeChar + "(.|$)", "g"), "\\$1");
        this.value = this.value.replace(new RegExp("\\" + singleChar, "g"), ".");
        this.value = this.value.replace(new RegExp("\\" + wildCard, "g"), ".*");
        this.value = this.value.replace(new RegExp("\\\\.\\*", "g"), "\\" + wildCard);
        this.value = this.value.replace(new RegExp("\\\\\\.", "g"), "\\" + singleChar);
        return this.value;
    },
    regex2value: function () {
        var value = this.value;
        value = value.replace(/!/g, "!!");
        value = value.replace(/(\\)?\\\./g, function ($0, $1) {
            return $1 ? $0 : "!.";
        });
        value = value.replace(/(\\)?\\\*/g, function ($0, $1) {
            return $1 ? $0 : "!*";
        });
        value = value.replace(/\\\\/g, "\\");
        value = value.replace(/\.\*/g, "*");
        return value;
    },
    clone: function () {
        return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this);
    },
    CLASS_NAME: "OpenLayers.Filter.Comparison"
});
OpenLayers.Filter.Comparison.EQUAL_TO = "==";
OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!=";
OpenLayers.Filter.Comparison.LESS_THAN = "<";
OpenLayers.Filter.Comparison.GREATER_THAN = ">";
OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<=";
OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">=";
OpenLayers.Filter.Comparison.BETWEEN = "..";
OpenLayers.Filter.Comparison.LIKE = "~";
OpenLayers.Format.Filter = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    CLASS_NAME: "OpenLayers.Format.Filter"
});
OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
    name: null,
    params: null,
    initialize: function (options) {
        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
    },
    CLASS_NAME: "OpenLayers.Filter.Function"
});
OpenLayers.Format.Filter.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ogc: "http://www.opengis.net/ogc",
        gml: "http://www.opengis.net/gml",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    defaultPrefix: "ogc",
    schemaLocation: null,
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        var obj = {};
        this.readers.ogc["Filter"].apply(this, [data, obj]);
        return obj.filter;
    },
    readers: {
        "ogc": {
            "Filter": function (node, parent) {
                var obj = {
                    fids: [],
                    filters: []
                };
                this.readChildNodes(node, obj);
                if (obj.fids.length > 0) {
                    parent.filter = new OpenLayers.Filter.FeatureId({
                        fids: obj.fids
                    });
                } else if (obj.filters.length > 0) {
                    parent.filter = obj.filters[0];
                }
            },
            "FeatureId": function (node, obj) {
                var fid = node.getAttribute("fid");
                if (fid) {
                    obj.fids.push(fid);
                }
            },
            "And": function (node, obj) {
                var filter = new OpenLayers.Filter.Logical({
                    type: OpenLayers.Filter.Logical.AND
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "Or": function (node, obj) {
                var filter = new OpenLayers.Filter.Logical({
                    type: OpenLayers.Filter.Logical.OR
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "Not": function (node, obj) {
                var filter = new OpenLayers.Filter.Logical({
                    type: OpenLayers.Filter.Logical.NOT
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsLessThan": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.LESS_THAN
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsGreaterThan": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.GREATER_THAN
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsLessThanOrEqualTo": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsGreaterThanOrEqualTo": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsBetween": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.BETWEEN
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "Literal": function (node, obj) {
                obj.value = OpenLayers.String.numericIf(this.getChildValue(node));
            },
            "PropertyName": function (node, filter) {
                filter.property = this.getChildValue(node);
            },
            "LowerBoundary": function (node, filter) {
                filter.lowerBoundary = OpenLayers.String.numericIf(this.readOgcExpression(node));
            },
            "UpperBoundary": function (node, filter) {
                filter.upperBoundary = OpenLayers.String.numericIf(this.readOgcExpression(node));
            },
            "Intersects": function (node, obj) {
                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.INTERSECTS);
            },
            "Within": function (node, obj) {
                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.WITHIN);
            },
            "Contains": function (node, obj) {
                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.CONTAINS);
            },
            "DWithin": function (node, obj) {
                this.readSpatial(node, obj, OpenLayers.Filter.Spatial.DWITHIN);
            },
            "Distance": function (node, obj) {
                obj.distance = parseInt(this.getChildValue(node));
                obj.distanceUnits = node.getAttribute("units");
            },
            "Function": function (node, obj) {
                return;
            }
        }
    },
    readSpatial: function (node, obj, type) {
        var filter = new OpenLayers.Filter.Spatial({
            type: type
        });
        this.readChildNodes(node, filter);
        filter.value = filter.components[0];
        delete filter.components;
        obj.filters.push(filter);
    },
    readOgcExpression: function (node) {
        var obj = {};
        this.readChildNodes(node, obj);
        var value = obj.value;
        if (value === undefined) {
            value = this.getChildValue(node);
        }
        return value;
    },
    writeOgcExpression: function (value, node) {
        if (value instanceof OpenLayers.Filter.Function) {
            var child = this.writeNode("Function", value, node);
            node.appendChild(child);
        } else {
            this.writeNode("Literal", value, node);
        }
        return node;
    },
    write: function (filter) {
        return this.writers.ogc["Filter"].apply(this, [filter]);
    },
    writeFeatureIdNodes: function (filter, node) {
        for (var i = 0, ii = filter.fids.length; i < ii; ++i) {
            this.writeNode("FeatureId", filter.fids[i], node);
        }
    },
    writers: {
        "ogc": {
            "Filter": function (filter) {
                var node = this.createElementNSPlus("ogc:Filter");
                if (filter.type === "FID") {
                    this.writeFeatureIdNodes(filter, node);
                } else {
                    this.writeNode(this.getFilterType(filter), filter, node);
                }
                return node;
            },
            "FeatureId": function (fid) {
                return this.createElementNSPlus("ogc:FeatureId", {
                    attributes: {
                        fid: fid
                    }
                });
            },
            "And": function (filter) {
                var node = this.createElementNSPlus("ogc:And");
                var childFilter;
                for (var i = 0, ii = filter.filters.length; i < ii; ++i) {
                    childFilter = filter.filters[i];
                    if (childFilter.type === "FID") {
                        this.writeFeatureIdNodes(childFilter, node);
                    } else {
                        this.writeNode(this.getFilterType(childFilter), childFilter, node);
                    }
                }
                return node;
            },
            "Or": function (filter) {
                var node = this.createElementNSPlus("ogc:Or");
                var childFilter;
                for (var i = 0, ii = filter.filters.length; i < ii; ++i) {
                    childFilter = filter.filters[i];
                    if (childFilter.type === "FID") {
                        this.writeFeatureIdNodes(childFilter, node);
                    } else {
                        this.writeNode(this.getFilterType(childFilter), childFilter, node);
                    }
                }
                return node;
            },
            "Not": function (filter) {
                var node = this.createElementNSPlus("ogc:Not");
                var childFilter = filter.filters[0];
                if (childFilter.type === "FID") {
                    this.writeFeatureIdNodes(childFilter, node);
                } else {
                    this.writeNode(this.getFilterType(childFilter), childFilter, node);
                }
                return node;
            },
            "PropertyIsLessThan": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsGreaterThan": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsLessThanOrEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsGreaterThanOrEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsBetween": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsBetween");
                this.writeNode("PropertyName", filter, node);
                this.writeNode("LowerBoundary", filter, node);
                this.writeNode("UpperBoundary", filter, node);
                return node;
            },
            "PropertyName": function (filter) {
                return this.createElementNSPlus("ogc:PropertyName", {
                    value: filter.property
                });
            },
            "Literal": function (value) {
                return this.createElementNSPlus("ogc:Literal", {
                    value: value
                });
            },
            "LowerBoundary": function (filter) {
                var node = this.createElementNSPlus("ogc:LowerBoundary");
                this.writeOgcExpression(filter.lowerBoundary, node);
                return node;
            },
            "UpperBoundary": function (filter) {
                var node = this.createElementNSPlus("ogc:UpperBoundary");
                this.writeNode("Literal", filter.upperBoundary, node);
                return node;
            },
            "INTERSECTS": function (filter) {
                return this.writeSpatial(filter, "Intersects");
            },
            "WITHIN": function (filter) {
                return this.writeSpatial(filter, "Within");
            },
            "CONTAINS": function (filter) {
                return this.writeSpatial(filter, "Contains");
            },
            "DWITHIN": function (filter) {
                var node = this.writeSpatial(filter, "DWithin");
                this.writeNode("Distance", filter, node);
                return node;
            },
            "Distance": function (filter) {
                return this.createElementNSPlus("ogc:Distance", {
                    attributes: {
                        units: filter.distanceUnits
                    },
                    value: filter.distance
                });
            },
            "Function": function (filter) {
                var node = this.createElementNSPlus("ogc:Function", {
                    attributes: {
                        name: filter.name
                    }
                });
                var params = filter.params;
                for (var i = 0, len = params.length; i < len; i++) {
                    this.writeOgcExpression(params[i], node);
                }
                return node;
            }
        }
    },
    getFilterType: function (filter) {
        var filterType = this.filterMap[filter.type];
        if (!filterType) {
            throw "Filter writing not supported for rule type: " + filter.type;
        }
        return filterType;
    },
    filterMap: {
        "&&": "And",
        "||": "Or",
        "!": "Not",
        "==": "PropertyIsEqualTo",
        "!=": "PropertyIsNotEqualTo",
        "<": "PropertyIsLessThan",
        ">": "PropertyIsGreaterThan",
        "<=": "PropertyIsLessThanOrEqualTo",
        ">=": "PropertyIsGreaterThanOrEqualTo",
        "..": "PropertyIsBetween",
        "~": "PropertyIsLike",
        "BBOX": "BBOX",
        "DWITHIN": "DWITHIN",
        "WITHIN": "WITHIN",
        "CONTAINS": "CONTAINS",
        "INTERSECTS": "INTERSECTS",
        "FID": "FeatureId"
    },
    CLASS_NAME: "OpenLayers.Format.Filter.v1"
});
OpenLayers.Geometry = OpenLayers.Class({
    id: null,
    parent: null,
    bounds: null,
    initialize: function () {
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    destroy: function () {
        this.id = null;
        this.bounds = null;
    },
    clone: function () {
        return new OpenLayers.Geometry();
    },
    setBounds: function (bounds) {
        if (bounds) {
            this.bounds = bounds.clone();
        }
    },
    clearBounds: function () {
        this.bounds = null;
        if (this.parent) {
            this.parent.clearBounds();
        }
    },
    extendBounds: function (newBounds) {
        var bounds = this.getBounds();
        if (!bounds) {
            this.setBounds(newBounds);
        } else {
            this.bounds.extend(newBounds);
        }
    },
    getBounds: function () {
        if (this.bounds == null) {
            this.calculateBounds();
        }
        return this.bounds;
    },
    calculateBounds: function () {},
    distanceTo: function (geometry, options) {},
    getVertices: function (nodes) {},
    atPoint: function (lonlat, toleranceLon, toleranceLat) {
        var atPoint = false;
        var bounds = this.getBounds();
        if ((bounds != null) && (lonlat != null)) {
            var dX = (toleranceLon != null) ? toleranceLon : 0;
            var dY = (toleranceLat != null) ? toleranceLat : 0;
            var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY);
            atPoint = toleranceBounds.containsLonLat(lonlat);
        }
        return atPoint;
    },
    getLength: function () {
        return 0.0;
    },
    getArea: function () {
        return 0.0;
    },
    getCentroid: function () {
        return null;
    },
    toString: function () {
        return OpenLayers.Format.WKT.prototype.write(new OpenLayers.Feature.Vector(this));
    },
    CLASS_NAME: "OpenLayers.Geometry"
});
OpenLayers.Geometry.fromWKT = function (wkt) {
    var format = arguments.callee.format;
    if (!format) {
        format = new OpenLayers.Format.WKT();
        arguments.callee.format = format;
    }
    var geom;
    var result = format.read(wkt);
    if (result instanceof OpenLayers.Feature.Vector) {
        geom = result.geometry;
    } else if (OpenLayers.Util.isArray(result)) {
        var len = result.length;
        var components = new Array(len);
        for (var i = 0; i < len; ++i) {
            components[i] = result[i].geometry;
        }
        geom = new OpenLayers.Geometry.Collection(components);
    }
    return geom;
};
OpenLayers.Geometry.segmentsIntersect = function (seg1, seg2, options) {
    var point = options && options.point;
    var tolerance = options && options.tolerance;
    var intersection = false;
    var x11_21 = seg1.x1 - seg2.x1;
    var y11_21 = seg1.y1 - seg2.y1;
    var x12_11 = seg1.x2 - seg1.x1;
    var y12_11 = seg1.y2 - seg1.y1;
    var y22_21 = seg2.y2 - seg2.y1;
    var x22_21 = seg2.x2 - seg2.x1;
    var d = (y22_21 * x12_11) - (x22_21 * y12_11);
    var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
    var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
    if (d == 0) {
        if (n1 == 0 && n2 == 0) {
            intersection = true;
        }
    } else {
        var along1 = n1 / d;
        var along2 = n2 / d;
        if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) {
            if (!point) {
                intersection = true;
            } else {
                var x = seg1.x1 + (along1 * x12_11);
                var y = seg1.y1 + (along1 * y12_11);
                intersection = new OpenLayers.Geometry.Point(x, y);
            }
        }
    }
    if (tolerance) {
        var dist;
        if (intersection) {
            if (point) {
                var segs = [seg1, seg2];
                var seg, x, y;
                outer: for (var i = 0; i < 2; ++i) {
                    seg = segs[i];
                    for (var j = 1; j < 3; ++j) {
                        x = seg["x" + j];
                        y = seg["y" + j];
                        dist = Math.sqrt(Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2));
                        if (dist < tolerance) {
                            intersection.x = x;
                            intersection.y = y;
                            break outer;
                        }
                    }
                }
            }
        } else {
            var segs = [seg1, seg2];
            var source, target, x, y, p, result;
            outer: for (var i = 0; i < 2; ++i) {
                source = segs[i];
                target = segs[(i + 1) % 2];
                for (var j = 1; j < 3; ++j) {
                    p = {
                        x: source["x" + j],
                        y: source["y" + j]
                    };
                    result = OpenLayers.Geometry.distanceToSegment(p, target);
                    if (result.distance < tolerance) {
                        if (point) {
                            intersection = new OpenLayers.Geometry.Point(p.x, p.y);
                        } else {
                            intersection = true;
                        }
                        break outer;
                    }
                }
            }
        }
    }
    return intersection;
};
OpenLayers.Geometry.distanceToSegment = function (point, segment) {
    var x0 = point.x;
    var y0 = point.y;
    var x1 = segment.x1;
    var y1 = segment.y1;
    var x2 = segment.x2;
    var y2 = segment.y2;
    var dx = x2 - x1;
    var dy = y2 - y1;
    var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / (Math.pow(dx, 2) + Math.pow(dy, 2));
    var x, y;
    if (along <= 0.0) {
        x = x1;
        y = y1;
    } else if (along >= 1.0) {
        x = x2;
        y = y2;
    } else {
        x = x1 + along * dx;
        y = y1 + along * dy;
    }
    return {
        distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
        x: x,
        y: y
    };
};
OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
    x: null,
    y: null,
    initialize: function (x, y) {
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
        this.x = parseFloat(x);
        this.y = parseFloat(y);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Geometry.Point(this.x, this.y);
        }
        OpenLayers.Util.applyDefaults(obj, this);
        return obj;
    },
    calculateBounds: function () {
        this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x, this.y);
    },
    distanceTo: function (geometry, options) {
        var edge = !(options && options.edge === false);
        var details = edge && options && options.details;
        var distance, x0, y0, x1, y1, result;
        if (geometry instanceof OpenLayers.Geometry.Point) {
            x0 = this.x;
            y0 = this.y;
            x1 = geometry.x;
            y1 = geometry.y;
            distance = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));
            result = !details ? distance : {
                x0: x0,
                y0: y0,
                x1: x1,
                y1: y1,
                distance: distance
            };
        } else {
            result = geometry.distanceTo(this, options);
            if (details) {
                result = {
                    x0: result.x1,
                    y0: result.y1,
                    x1: result.x0,
                    y1: result.y0,
                    distance: result.distance
                };
            }
        }
        return result;
    },
    equals: function (geom) {
        var equals = false;
        if (geom != null) {
            equals = ((this.x == geom.x && this.y == geom.y) || (isNaN(this.x) && isNaN(this.y) && isNaN(geom.x) && isNaN(geom.y)));
        }
        return equals;
    },
    toShortString: function () {
        return (this.x + ", " + this.y);
    },
    move: function (x, y) {
        this.x = this.x + x;
        this.y = this.y + y;
        this.clearBounds();
    },
    rotate: function (angle, origin) {
        angle *= Math.PI / 180;
        var radius = this.distanceTo(origin);
        var theta = angle + Math.atan2(this.y - origin.y, this.x - origin.x);
        this.x = origin.x + (radius * Math.cos(theta));
        this.y = origin.y + (radius * Math.sin(theta));
        this.clearBounds();
    },
    getCentroid: function () {
        return new OpenLayers.Geometry.Point(this.x, this.y);
    },
    resize: function (scale, origin, ratio) {
        ratio = (ratio == undefined) ? 1 : ratio;
        this.x = origin.x + (scale * ratio * (this.x - origin.x));
        this.y = origin.y + (scale * (this.y - origin.y));
        this.clearBounds();
        return this;
    },
    intersects: function (geometry) {
        var intersect = false;
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            intersect = this.equals(geometry);
        } else {
            intersect = geometry.intersects(this);
        }
        return intersect;
    },
    transform: function (source, dest) {
        if ((source && dest)) {
            OpenLayers.Projection.transform(this, source, dest);
            this.bounds = null;
        }
        return this;
    },
    getVertices: function (nodes) {
        return [this];
    },
    CLASS_NAME: "OpenLayers.Geometry.Point"
});
OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
    components: null,
    componentTypes: null,
    initialize: function (components) {
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
        this.components = [];
        if (components != null) {
            this.addComponents(components);
        }
    },
    destroy: function () {
        this.components.length = 0;
        this.components = null;
        OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
    },
    clone: function () {
        var geometry = eval("new " + this.CLASS_NAME + "()");
        for (var i = 0, len = this.components.length; i < len; i++) {
            geometry.addComponent(this.components[i].clone());
        }
        OpenLayers.Util.applyDefaults(geometry, this);
        return geometry;
    },
    getComponentsString: function () {
        var strings = [];
        for (var i = 0, len = this.components.length; i < len; i++) {
            strings.push(this.components[i].toShortString());
        }
        return strings.join(",");
    },
    calculateBounds: function () {
        this.bounds = null;
        var bounds = new OpenLayers.Bounds();
        var components = this.components;
        if (components) {
            for (var i = 0, len = components.length; i < len; i++) {
                bounds.extend(components[i].getBounds());
            }
        }
        if (bounds.left != null && bounds.bottom != null && bounds.right != null && bounds.top != null) {
            this.setBounds(bounds);
        }
    },
    addComponents: function (components) {
        if (!(OpenLayers.Util.isArray(components))) {
            components = [components];
        }
        for (var i = 0, len = components.length; i < len; i++) {
            this.addComponent(components[i]);
        }
    },
    addComponent: function (component, index) {
        var added = false;
        if (component) {
            if (this.componentTypes == null || (OpenLayers.Util.indexOf(this.componentTypes, component.CLASS_NAME) > -1)) {
                if (index != null && (index < this.components.length)) {
                    var components1 = this.components.slice(0, index);
                    var components2 = this.components.slice(index, this.components.length);
                    components1.push(component);
                    this.components = components1.concat(components2);
                } else {
                    this.components.push(component);
                }
                component.parent = this;
                this.clearBounds();
                added = true;
            }
        }
        return added;
    },
    removeComponents: function (components) {
        var removed = false;
        if (!(OpenLayers.Util.isArray(components))) {
            components = [components];
        }
        for (var i = components.length - 1; i >= 0; --i) {
            removed = this.removeComponent(components[i]) || removed;
        }
        return removed;
    },
    removeComponent: function (component) {
        OpenLayers.Util.removeItem(this.components, component);
        this.clearBounds();
        return true;
    },
    getLength: function () {
        var length = 0.0;
        for (var i = 0, len = this.components.length; i < len; i++) {
            length += this.components[i].getLength();
        }
        return length;
    },
    getArea: function () {
        var area = 0.0;
        for (var i = 0, len = this.components.length; i < len; i++) {
            area += this.components[i].getArea();
        }
        return area;
    },
    getGeodesicArea: function (projection) {
        var area = 0.0;
        for (var i = 0, len = this.components.length; i < len; i++) {
            area += this.components[i].getGeodesicArea(projection);
        }
        return area;
    },
    getCentroid: function (weighted) {
        if (!weighted) {
            return this.components.length && this.components[0].getCentroid();
        }
        var len = this.components.length;
        if (!len) {
            return false;
        }
        var areas = [];
        var centroids = [];
        var areaSum = 0;
        var minArea = Number.MAX_VALUE;
        var component;
        for (var i = 0; i < len; ++i) {
            component = this.components[i];
            var area = component.getArea();
            var centroid = component.getCentroid(true);
            if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
                continue;
            }
            areas.push(area);
            areaSum += area;
            minArea = (area < minArea && area > 0) ? area : minArea;
            centroids.push(centroid);
        }
        len = areas.length;
        if (areaSum === 0) {
            for (var i = 0; i < len; ++i) {
                areas[i] = 1;
            }
            areaSum = areas.length;
        } else {
            for (var i = 0; i < len; ++i) {
                areas[i] /= minArea;
            }
            areaSum /= minArea;
        }
        var xSum = 0,
            ySum = 0,
            centroid, area;
        for (var i = 0; i < len; ++i) {
            centroid = centroids[i];
            area = areas[i];
            xSum += centroid.x * area;
            ySum += centroid.y * area;
        }
        return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum);
    },
    getGeodesicLength: function (projection) {
        var length = 0.0;
        for (var i = 0, len = this.components.length; i < len; i++) {
            length += this.components[i].getGeodesicLength(projection);
        }
        return length;
    },
    move: function (x, y) {
        for (var i = 0, len = this.components.length; i < len; i++) {
            this.components[i].move(x, y);
        }
    },
    rotate: function (angle, origin) {
        for (var i = 0, len = this.components.length; i < len; ++i) {
            this.components[i].rotate(angle, origin);
        }
    },
    resize: function (scale, origin, ratio) {
        for (var i = 0; i < this.components.length; ++i) {
            this.components[i].resize(scale, origin, ratio);
        }
        return this;
    },
    distanceTo: function (geometry, options) {
        var edge = !(options && options.edge === false);
        var details = edge && options && options.details;
        var result, best, distance;
        var min = Number.POSITIVE_INFINITY;
        for (var i = 0, len = this.components.length; i < len; ++i) {
            result = this.components[i].distanceTo(geometry, options);
            distance = details ? result.distance : result;
            if (distance < min) {
                min = distance;
                best = result;
                if (min == 0) {
                    break;
                }
            }
        }
        return best;
    },
    equals: function (geometry) {
        var equivalent = true;
        if (!geometry || !geometry.CLASS_NAME || (this.CLASS_NAME != geometry.CLASS_NAME)) {
            equivalent = false;
        } else if (!(OpenLayers.Util.isArray(geometry.components)) || (geometry.components.length != this.components.length)) {
            equivalent = false;
        } else {
            for (var i = 0, len = this.components.length; i < len; ++i) {
                if (!this.components[i].equals(geometry.components[i])) {
                    equivalent = false;
                    break;
                }
            }
        }
        return equivalent;
    },
    transform: function (source, dest) {
        if (source && dest) {
            for (var i = 0, len = this.components.length; i < len; i++) {
                var component = this.components[i];
                component.transform(source, dest);
            }
            this.bounds = null;
        }
        return this;
    },
    intersects: function (geometry) {
        var intersect = false;
        for (var i = 0, len = this.components.length; i < len; ++i) {
            intersect = geometry.intersects(this.components[i]);
            if (intersect) {
                break;
            }
        }
        return intersect;
    },
    getVertices: function (nodes) {
        var vertices = [];
        for (var i = 0, len = this.components.length; i < len; ++i) {
            Array.prototype.push.apply(vertices, this.components[i].getVertices(nodes));
        }
        return vertices;
    },
    CLASS_NAME: "OpenLayers.Geometry.Collection"
});
OpenLayers.Geometry.MultiPoint = OpenLayers.Class(OpenLayers.Geometry.Collection, {
    componentTypes: ["OpenLayers.Geometry.Point"],
    initialize: function (components) {
        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, arguments);
    },
    addPoint: function (point, index) {
        this.addComponent(point, index);
    },
    removePoint: function (point) {
        this.removeComponent(point);
    },
    CLASS_NAME: "OpenLayers.Geometry.MultiPoint"
});
OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, {
    componentTypes: ["OpenLayers.Geometry.Point"],
    initialize: function (points) {
        OpenLayers.Geometry.MultiPoint.prototype.initialize.apply(this, arguments);
    },
    getLength: function () {
        var length = 0.0;
        if (this.components && (this.components.length > 1)) {
            for (var i = 1, len = this.components.length; i < len; i++) {
                length += this.components[i - 1].distanceTo(this.components[i]);
            }
        }
        return length;
    },
    getGeodesicLength: function (projection) {
        var geom = this;
        if (projection) {
            var gg = new OpenLayers.Projection("EPSG:4326");
            if (!gg.equals(projection)) {
                geom = this.clone().transform(projection, gg);
            }
        }
        var length = 0.0;
        if (geom.components && (geom.components.length > 1)) {
            var p1, p2;
            for (var i = 1, len = geom.components.length; i < len; i++) {
                p1 = geom.components[i - 1];
                p2 = geom.components[i];
                length += OpenLayers.Util.distVincenty({
                    lon: p1.x,
                    lat: p1.y
                }, {
                    lon: p2.x,
                    lat: p2.y
                });
            }
        }
        return length * 1000;
    },
    CLASS_NAME: "OpenLayers.Geometry.Curve"
});
OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
    initialize: function (points) {
        OpenLayers.Geometry.Curve.prototype.initialize.apply(this, arguments);
    },
    removeComponent: function (point) {
        var removed = this.components && (this.components.length > 2);
        if (removed) {
            OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments);
        }
        return removed;
    },
    intersects: function (geometry) {
        var intersect = false;
        var type = geometry.CLASS_NAME;
        if (type == "OpenLayers.Geometry.LineString" || type == "OpenLayers.Geometry.LinearRing" || type == "OpenLayers.Geometry.Point") {
            var segs1 = this.getSortedSegments();
            var segs2;
            if (type == "OpenLayers.Geometry.Point") {
                segs2 = [{
                    x1: geometry.x,
                    y1: geometry.y,
                    x2: geometry.x,
                    y2: geometry.y
                }];
            } else {
                segs2 = geometry.getSortedSegments();
            }
            var seg1, seg1x1, seg1x2, seg1y1, seg1y2, seg2, seg2y1, seg2y2;
            outer: for (var i = 0, len = segs1.length; i < len; ++i) {
                seg1 = segs1[i];
                seg1x1 = seg1.x1;
                seg1x2 = seg1.x2;
                seg1y1 = seg1.y1;
                seg1y2 = seg1.y2;
                inner: for (var j = 0, jlen = segs2.length; j < jlen; ++j) {
                    seg2 = segs2[j];
                    if (seg2.x1 > seg1x2) {
                        break;
                    }
                    if (seg2.x2 < seg1x1) {
                        continue;
                    }
                    seg2y1 = seg2.y1;
                    seg2y2 = seg2.y2;
                    if (Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
                        continue;
                    }
                    if (Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
                        continue;
                    }
                    if (OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
                        intersect = true;
                        break outer;
                    }
                }
            }
        } else {
            intersect = geometry.intersects(this);
        }
        return intersect;
    },
    getSortedSegments: function () {
        var numSeg = this.components.length - 1;
        var segments = new Array(numSeg),
            point1, point2;
        for (var i = 0; i < numSeg; ++i) {
            point1 = this.components[i];
            point2 = this.components[i + 1];
            if (point1.x < point2.x) {
                segments[i] = {
                    x1: point1.x,
                    y1: point1.y,
                    x2: point2.x,
                    y2: point2.y
                };
            } else {
                segments[i] = {
                    x1: point2.x,
                    y1: point2.y,
                    x2: point1.x,
                    y2: point1.y
                };
            }
        }

        function byX1(seg1, seg2) {
            return seg1.x1 - seg2.x1;
        }
        return segments.sort(byX1);
    },
    splitWithSegment: function (seg, options) {
        var edge = !(options && options.edge === false);
        var tolerance = options && options.tolerance;
        var lines = [];
        var verts = this.getVertices();
        var points = [];
        var intersections = [];
        var split = false;
        var vert1, vert2, point;
        var node, vertex, target;
        var interOptions = {
            point: true,
            tolerance: tolerance
        };
        var result = null;
        for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {
            vert1 = verts[i];
            points.push(vert1.clone());
            vert2 = verts[i + 1];
            target = {
                x1: vert1.x,
                y1: vert1.y,
                x2: vert2.x,
                y2: vert2.y
            };
            point = OpenLayers.Geometry.segmentsIntersect(seg, target, interOptions);
            if (point instanceof OpenLayers.Geometry.Point) {
                if ((point.x === seg.x1 && point.y === seg.y1) || (point.x === seg.x2 && point.y === seg.y2) || point.equals(vert1) || point.equals(vert2)) {
                    vertex = true;
                } else {
                    vertex = false;
                }
                if (vertex || edge) {
                    if (!point.equals(intersections[intersections.length - 1])) {
                        intersections.push(point.clone());
                    }
                    if (i === 0) {
                        if (point.equals(vert1)) {
                            continue;
                        }
                    }
                    if (point.equals(vert2)) {
                        continue;
                    }
                    split = true;
                    if (!point.equals(vert1)) {
                        points.push(point);
                    }
                    lines.push(new OpenLayers.Geometry.LineString(points));
                    points = [point.clone()];
                }
            }
        }
        if (split) {
            points.push(vert2.clone());
            lines.push(new OpenLayers.Geometry.LineString(points));
        }
        if (intersections.length > 0) {
            var xDir = seg.x1 < seg.x2 ? 1 : -1;
            var yDir = seg.y1 < seg.y2 ? 1 : -1;
            result = {
                lines: lines,
                points: intersections.sort(function (p1, p2) {
                    return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
                })
            };
        }
        return result;
    },
    split: function (target, options) {
        var results = null;
        var mutual = options && options.mutual;
        var sourceSplit, targetSplit, sourceParts, targetParts;
        if (target instanceof OpenLayers.Geometry.LineString) {
            var verts = this.getVertices();
            var vert1, vert2, seg, splits, lines, point;
            var points = [];
            sourceParts = [];
            for (var i = 0, stop = verts.length - 2; i <= stop; ++i) {
                vert1 = verts[i];
                vert2 = verts[i + 1];
                seg = {
                    x1: vert1.x,
                    y1: vert1.y,
                    x2: vert2.x,
                    y2: vert2.y
                };
                targetParts = targetParts || [target];
                if (mutual) {
                    points.push(vert1.clone());
                }
                for (var j = 0; j < targetParts.length; ++j) {
                    splits = targetParts[j].splitWithSegment(seg, options);
                    if (splits) {
                        lines = splits.lines;
                        if (lines.length > 0) {
                            lines.unshift(j, 1);
                            Array.prototype.splice.apply(targetParts, lines);
                            j += lines.length - 2;
                        }
                        if (mutual) {
                            for (var k = 0, len = splits.points.length; k < len; ++k) {
                                point = splits.points[k];
                                if (!point.equals(vert1)) {
                                    points.push(point);
                                    sourceParts.push(new OpenLayers.Geometry.LineString(points));
                                    if (point.equals(vert2)) {
                                        points = [];
                                    } else {
                                        points = [point.clone()];
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (mutual && sourceParts.length > 0 && points.length > 0) {
                points.push(vert2.clone());
                sourceParts.push(new OpenLayers.Geometry.LineString(points));
            }
        } else {
            results = target.splitWith(this, options);
        }
        if (targetParts && targetParts.length > 1) {
            targetSplit = true;
        } else {
            targetParts = [];
        }
        if (sourceParts && sourceParts.length > 1) {
            sourceSplit = true;
        } else {
            sourceParts = [];
        }
        if (targetSplit || sourceSplit) {
            if (mutual) {
                results = [sourceParts, targetParts];
            } else {
                results = targetParts;
            }
        }
        return results;
    },
    splitWith: function (geometry, options) {
        return geometry.split(this, options);
    },
    getVertices: function (nodes) {
        var vertices;
        if (nodes === true) {
            vertices = [this.components[0], this.components[this.components.length - 1]];
        } else if (nodes === false) {
            vertices = this.components.slice(1, this.components.length - 1);
        } else {
            vertices = this.components.slice();
        }
        return vertices;
    },
    distanceTo: function (geometry, options) {
        var edge = !(options && options.edge === false);
        var details = edge && options && options.details;
        var result, best = {};
        var min = Number.POSITIVE_INFINITY;
        if (geometry instanceof OpenLayers.Geometry.Point) {
            var segs = this.getSortedSegments();
            var x = geometry.x;
            var y = geometry.y;
            var seg;
            for (var i = 0, len = segs.length; i < len; ++i) {
                seg = segs[i];
                result = OpenLayers.Geometry.distanceToSegment(geometry, seg);
                if (result.distance < min) {
                    min = result.distance;
                    best = result;
                    if (min === 0) {
                        break;
                    }
                } else {
                    if (seg.x2 > x && ((y > seg.y1 && y < seg.y2) || (y < seg.y1 && y > seg.y2))) {
                        break;
                    }
                }
            }
            if (details) {
                best = {
                    distance: best.distance,
                    x0: best.x,
                    y0: best.y,
                    x1: x,
                    y1: y
                };
            } else {
                best = best.distance;
            }
        } else if (geometry instanceof OpenLayers.Geometry.LineString) {
            var segs0 = this.getSortedSegments();
            var segs1 = geometry.getSortedSegments();
            var seg0, seg1, intersection, x0, y0;
            var len1 = segs1.length;
            var interOptions = {
                point: true
            };
            outer: for (var i = 0, len = segs0.length; i < len; ++i) {
                seg0 = segs0[i];
                x0 = seg0.x1;
                y0 = seg0.y1;
                for (var j = 0; j < len1; ++j) {
                    seg1 = segs1[j];
                    intersection = OpenLayers.Geometry.segmentsIntersect(seg0, seg1, interOptions);
                    if (intersection) {
                        min = 0;
                        best = {
                            distance: 0,
                            x0: intersection.x,
                            y0: intersection.y,
                            x1: intersection.x,
                            y1: intersection.y
                        };
                        break outer;
                    } else {
                        result = OpenLayers.Geometry.distanceToSegment({
                            x: x0,
                            y: y0
                        }, seg1);
                        if (result.distance < min) {
                            min = result.distance;
                            best = {
                                distance: min,
                                x0: x0,
                                y0: y0,
                                x1: result.x,
                                y1: result.y
                            };
                        }
                    }
                }
            }
            if (!details) {
                best = best.distance;
            }
            if (min !== 0) {
                if (seg0) {
                    result = geometry.distanceTo(new OpenLayers.Geometry.Point(seg0.x2, seg0.y2), options);
                    var dist = details ? result.distance : result;
                    if (dist < min) {
                        if (details) {
                            best = {
                                distance: min,
                                x0: result.x1,
                                y0: result.y1,
                                x1: result.x0,
                                y1: result.y0
                            };
                        } else {
                            best = dist;
                        }
                    }
                }
            }
        } else {
            best = geometry.distanceTo(this, options);
            if (details) {
                best = {
                    distance: best.distance,
                    x0: best.x1,
                    y0: best.y1,
                    x1: best.x0,
                    y1: best.y0
                };
            }
        }
        return best;
    },
    simplify: function (tolerance) {
        if (this && this !== null) {
            var points = this.getVertices();
            if (points.length < 3) {
                return this;
            }
            var compareNumbers = function (a, b) {
                    return (a - b);
                };
            var douglasPeuckerReduction = function (points, firstPoint, lastPoint, tolerance) {
                    var maxDistance = 0;
                    var indexFarthest = 0;
                    for (var index = firstPoint, distance; index < lastPoint; index++) {
                        distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]);
                        if (distance > maxDistance) {
                            maxDistance = distance;
                            indexFarthest = index;
                        }
                    }
                    if (maxDistance > tolerance && indexFarthest != firstPoint) {
                        pointIndexsToKeep.push(indexFarthest);
                        douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance);
                        douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance);
                    }
                };
            var perpendicularDistance = function (point1, point2, point) {
                    var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y));
                    var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
                    var height = area / bottom * 2;
                    return height;
                };
            var firstPoint = 0;
            var lastPoint = points.length - 1;
            var pointIndexsToKeep = [];
            pointIndexsToKeep.push(firstPoint);
            pointIndexsToKeep.push(lastPoint);
            while (points[firstPoint].equals(points[lastPoint])) {
                lastPoint--;
                pointIndexsToKeep.push(lastPoint);
            }
            douglasPeuckerReduction(points, firstPoint, lastPoint, tolerance);
            var returnPoints = [];
            pointIndexsToKeep.sort(compareNumbers);
            for (var index = 0; index < pointIndexsToKeep.length; index++) {
                returnPoints.push(points[pointIndexsToKeep[index]]);
            }
            return new OpenLayers.Geometry.LineString(returnPoints);
        } else {
            return this;
        }
    },
    CLASS_NAME: "OpenLayers.Geometry.LineString"
});
OpenLayers.Geometry.MultiLineString = OpenLayers.Class(OpenLayers.Geometry.Collection, {
    componentTypes: ["OpenLayers.Geometry.LineString"],
    initialize: function (components) {
        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, arguments);
    },
    split: function (geometry, options) {
        var results = null;
        var mutual = options && options.mutual;
        var splits, sourceLine, sourceLines, sourceSplit, targetSplit;
        var sourceParts = [];
        var targetParts = [geometry];
        for (var i = 0, len = this.components.length; i < len; ++i) {
            sourceLine = this.components[i];
            sourceSplit = false;
            for (var j = 0; j < targetParts.length; ++j) {
                splits = sourceLine.split(targetParts[j], options);
                if (splits) {
                    if (mutual) {
                        sourceLines = splits[0];
                        for (var k = 0, klen = sourceLines.length; k < klen; ++k) {
                            if (k === 0 && sourceParts.length) {
                                sourceParts[sourceParts.length - 1].addComponent(sourceLines[k]);
                            } else {
                                sourceParts.push(new OpenLayers.Geometry.MultiLineString([sourceLines[k]]));
                            }
                        }
                        sourceSplit = true;
                        splits = splits[1];
                    }
                    if (splits.length) {
                        splits.unshift(j, 1);
                        Array.prototype.splice.apply(targetParts, splits);
                        break;
                    }
                }
            }
            if (!sourceSplit) {
                if (sourceParts.length) {
                    sourceParts[sourceParts.length - 1].addComponent(sourceLine.clone());
                } else {
                    sourceParts = [new OpenLayers.Geometry.MultiLineString(sourceLine.clone())];
                }
            }
        }
        if (sourceParts && sourceParts.length > 1) {
            sourceSplit = true;
        } else {
            sourceParts = [];
        }
        if (targetParts && targetParts.length > 1) {
            targetSplit = true;
        } else {
            targetParts = [];
        }
        if (sourceSplit || targetSplit) {
            if (mutual) {
                results = [sourceParts, targetParts];
            } else {
                results = targetParts;
            }
        }
        return results;
    },
    splitWith: function (geometry, options) {
        var results = null;
        var mutual = options && options.mutual;
        var splits, targetLine, sourceLines, sourceSplit, targetSplit, sourceParts, targetParts;
        if (geometry instanceof OpenLayers.Geometry.LineString) {
            targetParts = [];
            sourceParts = [geometry];
            for (var i = 0, len = this.components.length; i < len; ++i) {
                targetSplit = false;
                targetLine = this.components[i];
                for (var j = 0; j < sourceParts.length; ++j) {
                    splits = sourceParts[j].split(targetLine, options);
                    if (splits) {
                        if (mutual) {
                            sourceLines = splits[0];
                            if (sourceLines.length) {
                                sourceLines.unshift(j, 1);
                                Array.prototype.splice.apply(sourceParts, sourceLines);
                                j += sourceLines.length - 2;
                            }
                            splits = splits[1];
                            if (splits.length === 0) {
                                splits = [targetLine.clone()];
                            }
                        }
                        for (var k = 0, klen = splits.length; k < klen; ++k) {
                            if (k === 0 && targetParts.length) {
                                targetParts[targetParts.length - 1].addComponent(splits[k]);
                            } else {
                                targetParts.push(new OpenLayers.Geometry.MultiLineString([splits[k]]));
                            }
                        }
                        targetSplit = true;
                    }
                }
                if (!targetSplit) {
                    if (targetParts.length) {
                        targetParts[targetParts.length - 1].addComponent(targetLine.clone());
                    } else {
                        targetParts = [new OpenLayers.Geometry.MultiLineString([targetLine.clone()])];
                    }
                }
            }
        } else {
            results = geometry.split(this);
        }
        if (sourceParts && sourceParts.length > 1) {
            sourceSplit = true;
        } else {
            sourceParts = [];
        }
        if (targetParts && targetParts.length > 1) {
            targetSplit = true;
        } else {
            targetParts = [];
        }
        if (sourceSplit || targetSplit) {
            if (mutual) {
                results = [sourceParts, targetParts];
            } else {
                results = targetParts;
            }
        }
        return results;
    },
    CLASS_NAME: "OpenLayers.Geometry.MultiLineString"
});
OpenLayers.Geometry.LinearRing = OpenLayers.Class(OpenLayers.Geometry.LineString, {
    componentTypes: ["OpenLayers.Geometry.Point"],
    initialize: function (points) {
        OpenLayers.Geometry.LineString.prototype.initialize.apply(this, arguments);
    },
    addComponent: function (point, index) {
        var added = false;
        var lastPoint = this.components.pop();
        if (index != null || !point.equals(lastPoint)) {
            added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, arguments);
        }
        var firstPoint = this.components[0];
        OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint]);
        return added;
    },
    removeComponent: function (point) {
        var removed = this.components && (this.components.length > 3);
        if (removed) {
            this.components.pop();
            OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, arguments);
            var firstPoint = this.components[0];
            OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, [firstPoint]);
        }
        return removed;
    },
    move: function (x, y) {
        for (var i = 0, len = this.components.length; i < len - 1; i++) {
            this.components[i].move(x, y);
        }
    },
    rotate: function (angle, origin) {
        for (var i = 0, len = this.components.length; i < len - 1; ++i) {
            this.components[i].rotate(angle, origin);
        }
    },
    resize: function (scale, origin, ratio) {
        for (var i = 0, len = this.components.length; i < len - 1; ++i) {
            this.components[i].resize(scale, origin, ratio);
        }
        return this;
    },
    transform: function (source, dest) {
        if (source && dest) {
            for (var i = 0, len = this.components.length; i < len - 1; i++) {
                var component = this.components[i];
                component.transform(source, dest);
            }
            this.bounds = null;
        }
        return this;
    },
    getCentroid: function () {
        if (this.components && (this.components.length > 2)) {
            var sumX = 0.0;
            var sumY = 0.0;
            for (var i = 0; i < this.components.length - 1; i++) {
                var b = this.components[i];
                var c = this.components[i + 1];
                sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
                sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
            }
            var area = -1 * this.getArea();
            var x = sumX / (6 * area);
            var y = sumY / (6 * area);
            return new OpenLayers.Geometry.Point(x, y);
        } else {
            return null;
        }
    },
    getArea: function () {
        var area = 0.0;
        if (this.components && (this.components.length > 2)) {
            var sum = 0.0;
            for (var i = 0, len = this.components.length; i < len - 1; i++) {
                var b = this.components[i];
                var c = this.components[i + 1];
                sum += (b.x + c.x) * (c.y - b.y);
            }
            area = -sum / 2.0;
        }
        return area;
    },
    getGeodesicArea: function (projection) {
        var ring = this;
        if (projection) {
            var gg = new OpenLayers.Projection("EPSG:4326");
            if (!gg.equals(projection)) {
                ring = this.clone().transform(projection, gg);
            }
        }
        var area = 0.0;
        var len = ring.components && ring.components.length;
        if (len > 2) {
            var p1, p2;
            for (var i = 0; i < len - 1; i++) {
                p1 = ring.components[i];
                p2 = ring.components[i + 1];
                area += OpenLayers.Util.rad(p2.x - p1.x) * (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + Math.sin(OpenLayers.Util.rad(p2.y)));
            }
            area = area * 6378137.0 * 6378137.0 / 2.0;
        }
        return area;
    },
    containsPoint: function (point) {
        var approx = OpenLayers.Number.limitSigDigs;
        var digs = 14;
        var px = approx(point.x, digs);
        var py = approx(point.y, digs);

        function getX(y, x1, y1, x2, y2) {
            return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
        }
        var numSeg = this.components.length - 1;
        var start, end, x1, y1, x2, y2, cx, cy;
        var crosses = 0;
        for (var i = 0; i < numSeg; ++i) {
            start = this.components[i];
            x1 = approx(start.x, digs);
            y1 = approx(start.y, digs);
            end = this.components[i + 1];
            x2 = approx(end.x, digs);
            y2 = approx(end.y, digs);
            if (y1 == y2) {
                if (py == y1) {
                    if (x1 <= x2 && (px >= x1 && px <= x2) || x1 >= x2 && (px <= x1 && px >= x2)) {
                        crosses = -1;
                        break;
                    }
                }
                continue;
            }
            cx = approx(getX(py, x1, y1, x2, y2), digs);
            if (cx == px) {
                if (y1 < y2 && (py >= y1 && py <= y2) || y1 > y2 && (py <= y1 && py >= y2)) {
                    crosses = -1;
                    break;
                }
            }
            if (cx <= px) {
                continue;
            }
            if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) {
                continue;
            }
            if (y1 < y2 && (py >= y1 && py < y2) || y1 > y2 && (py < y1 && py >= y2)) {
                ++crosses;
            }
        }
        var contained = (crosses == -1) ? 1 : !! (crosses & 1);
        return contained;
    },
    intersects: function (geometry) {
        var intersect = false;
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            intersect = this.containsPoint(geometry);
        } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
            intersect = geometry.intersects(this);
        } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
            intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply(this, [geometry]);
        } else {
            for (var i = 0, len = geometry.components.length; i < len; ++i) {
                intersect = geometry.components[i].intersects(this);
                if (intersect) {
                    break;
                }
            }
        }
        return intersect;
    },
    getVertices: function (nodes) {
        return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1);
    },
    CLASS_NAME: "OpenLayers.Geometry.LinearRing"
});
OpenLayers.Geometry.Polygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {
    componentTypes: ["OpenLayers.Geometry.LinearRing"],
    initialize: function (components) {
        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, arguments);
    },
    getArea: function () {
        var area = 0.0;
        if (this.components && (this.components.length > 0)) {
            area += Math.abs(this.components[0].getArea());
            for (var i = 1, len = this.components.length; i < len; i++) {
                area -= Math.abs(this.components[i].getArea());
            }
        }
        return area;
    },
    getGeodesicArea: function (projection) {
        var area = 0.0;
        if (this.components && (this.components.length > 0)) {
            area += Math.abs(this.components[0].getGeodesicArea(projection));
            for (var i = 1, len = this.components.length; i < len; i++) {
                area -= Math.abs(this.components[i].getGeodesicArea(projection));
            }
        }
        return area;
    },
    containsPoint: function (point) {
        var numRings = this.components.length;
        var contained = false;
        if (numRings > 0) {
            contained = this.components[0].containsPoint(point);
            if (contained !== 1) {
                if (contained && numRings > 1) {
                    var hole;
                    for (var i = 1; i < numRings; ++i) {
                        hole = this.components[i].containsPoint(point);
                        if (hole) {
                            if (hole === 1) {
                                contained = 1;
                            } else {
                                contained = false;
                            }
                            break;
                        }
                    }
                }
            }
        }
        return contained;
    },
    intersects: function (geometry) {
        var intersect = false;
        var i, len;
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            intersect = this.containsPoint(geometry);
        } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
            for (i = 0, len = this.components.length; i < len; ++i) {
                intersect = geometry.intersects(this.components[i]);
                if (intersect) {
                    break;
                }
            }
            if (!intersect) {
                for (i = 0, len = geometry.components.length; i < len; ++i) {
                    intersect = this.containsPoint(geometry.components[i]);
                    if (intersect) {
                        break;
                    }
                }
            }
        } else {
            for (i = 0, len = geometry.components.length; i < len; ++i) {
                intersect = this.intersects(geometry.components[i]);
                if (intersect) {
                    break;
                }
            }
        }
        if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
            var ring = this.components[0];
            for (i = 0, len = ring.components.length; i < len; ++i) {
                intersect = geometry.containsPoint(ring.components[i]);
                if (intersect) {
                    break;
                }
            }
        }
        return intersect;
    },
    distanceTo: function (geometry, options) {
        var edge = !(options && options.edge === false);
        var result;
        if (!edge && this.intersects(geometry)) {
            result = 0;
        } else {
            result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(this, [geometry, options]);
        }
        return result;
    },
    CLASS_NAME: "OpenLayers.Geometry.Polygon"
});
OpenLayers.Geometry.Polygon.createRegularPolygon = function (origin, radius, sides, rotation) {
    var angle = Math.PI * ((1 / sides) - (1 / 2));
    if (rotation) {
        angle += (rotation / 180) * Math.PI;
    }
    var rotatedAngle, x, y;
    var points = [];
    for (var i = 0; i < sides; ++i) {
        rotatedAngle = angle + (i * 2 * Math.PI / sides);
        x = origin.x + (radius * Math.cos(rotatedAngle));
        y = origin.y + (radius * Math.sin(rotatedAngle));
        points.push(new OpenLayers.Geometry.Point(x, y));
    }
    var ring = new OpenLayers.Geometry.LinearRing(points);
    return new OpenLayers.Geometry.Polygon([ring]);
};
OpenLayers.Geometry.MultiPolygon = OpenLayers.Class(OpenLayers.Geometry.Collection, {
    componentTypes: ["OpenLayers.Geometry.Polygon"],
    initialize: function (components) {
        OpenLayers.Geometry.Collection.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Geometry.MultiPolygon"
});
OpenLayers.Format.GML = OpenLayers.Class(OpenLayers.Format.XML, {
    featureNS: "http://mapserver.gis.umn.edu/mapserver",
    featurePrefix: "feature",
    featureName: "featureMember",
    layerName: "features",
    geometryName: "geometry",
    collectionName: "FeatureCollection",
    gmlns: "http://www.opengis.net/gml",
    extractAttributes: true,
    xy: true,
    initialize: function (options) {
        this.regExes = {
            trimSpace: (/^\s*|\s*$/g),
            removeSpace: (/\s*/g),
            splitSpace: (/\s+/),
            trimComma: (/\s*,\s*/g)
        };
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var featureNodes = this.getElementsByTagNameNS(data.documentElement, this.gmlns, this.featureName);
        var features = [];
        for (var i = 0; i < featureNodes.length; i++) {
            var feature = this.parseFeature(featureNodes[i]);
            if (feature) {
                features.push(feature);
            }
        }
        return features;
    },
    parseFeature: function (node) {
        var order = ["MultiPolygon", "Polygon", "MultiLineString", "LineString", "MultiPoint", "Point", "Envelope"];
        var type, nodeList, geometry, parser;
        for (var i = 0; i < order.length; ++i) {
            type = order[i];
            nodeList = this.getElementsByTagNameNS(node, this.gmlns, type);
            if (nodeList.length > 0) {
                parser = this.parseGeometry[type.toLowerCase()];
                if (parser) {
                    geometry = parser.apply(this, [nodeList[0]]);
                    if (this.internalProjection && this.externalProjection) {
                        geometry.transform(this.externalProjection, this.internalProjection);
                    }
                } else {
                    OpenLayers.Console.error(OpenLayers.i18n("unsupportedGeometryType", {
                        'geomType': type
                    }));
                }
                break;
            }
        }
        var bounds;
        var boxNodes = this.getElementsByTagNameNS(node, this.gmlns, "Box");
        for (i = 0; i < boxNodes.length; ++i) {
            var boxNode = boxNodes[i];
            var box = this.parseGeometry["box"].apply(this, [boxNode]);
            var parentNode = boxNode.parentNode;
            var parentName = parentNode.localName || parentNode.nodeName.split(":").pop();
            if (parentName === "boundedBy") {
                bounds = box;
            } else {
                geometry = box.toGeometry();
            }
        }
        var attributes;
        if (this.extractAttributes) {
            attributes = this.parseAttributes(node);
        }
        var feature = new OpenLayers.Feature.Vector(geometry, attributes);
        feature.bounds = bounds;
        feature.gml = {
            featureType: node.firstChild.nodeName.split(":")[1],
            featureNS: node.firstChild.namespaceURI,
            featureNSPrefix: node.firstChild.prefix
        };
        var childNode = node.firstChild;
        var fid;
        while (childNode) {
            if (childNode.nodeType == 1) {
                fid = childNode.getAttribute("fid") || childNode.getAttribute("id");
                if (fid) {
                    break;
                }
            }
            childNode = childNode.nextSibling;
        }
        feature.fid = fid;
        return feature;
    },
    parseGeometry: {
        point: function (node) {
            var nodeList, coordString;
            var coords = [];
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "pos");
            if (nodeList.length > 0) {
                coordString = nodeList[0].firstChild.nodeValue;
                coordString = coordString.replace(this.regExes.trimSpace, "");
                coords = coordString.split(this.regExes.splitSpace);
            }
            if (coords.length == 0) {
                nodeList = this.getElementsByTagNameNS(node, this.gmlns, "coordinates");
                if (nodeList.length > 0) {
                    coordString = nodeList[0].firstChild.nodeValue;
                    coordString = coordString.replace(this.regExes.removeSpace, "");
                    coords = coordString.split(",");
                }
            }
            if (coords.length == 0) {
                nodeList = this.getElementsByTagNameNS(node, this.gmlns, "coord");
                if (nodeList.length > 0) {
                    var xList = this.getElementsByTagNameNS(nodeList[0], this.gmlns, "X");
                    var yList = this.getElementsByTagNameNS(nodeList[0], this.gmlns, "Y");
                    if (xList.length > 0 && yList.length > 0) {
                        coords = [xList[0].firstChild.nodeValue, yList[0].firstChild.nodeValue];
                    }
                }
            }
            if (coords.length == 2) {
                coords[2] = null;
            }
            if (this.xy) {
                return new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
            } else {
                return new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);
            }
        },
        multipoint: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "Point");
            var components = [];
            if (nodeList.length > 0) {
                var point;
                for (var i = 0; i < nodeList.length; ++i) {
                    point = this.parseGeometry.point.apply(this, [nodeList[i]]);
                    if (point) {
                        components.push(point);
                    }
                }
            }
            return new OpenLayers.Geometry.MultiPoint(components);
        },
        linestring: function (node, ring) {
            var nodeList, coordString;
            var coords = [];
            var points = [];
            nodeList = this.getElementsByTagNameNS(node, this.gmlns, "posList");
            if (nodeList.length > 0) {
                coordString = this.getChildValue(nodeList[0]);
                coordString = coordString.replace(this.regExes.trimSpace, "");
                coords = coordString.split(this.regExes.splitSpace);
                var dim = parseInt(nodeList[0].getAttribute("dimension"));
                var j, x, y, z;
                for (var i = 0; i < coords.length / dim; ++i) {
                    j = i * dim;
                    x = coords[j];
                    y = coords[j + 1];
                    z = (dim == 2) ? null : coords[j + 2];
                    if (this.xy) {
                        points.push(new OpenLayers.Geometry.Point(x, y, z));
                    } else {
                        points.push(new OpenLayers.Geometry.Point(y, x, z));
                    }
                }
            }
            if (coords.length == 0) {
                nodeList = this.getElementsByTagNameNS(node, this.gmlns, "coordinates");
                if (nodeList.length > 0) {
                    coordString = this.getChildValue(nodeList[0]);
                    coordString = coordString.replace(this.regExes.trimSpace, "");
                    coordString = coordString.replace(this.regExes.trimComma, ",");
                    var pointList = coordString.split(this.regExes.splitSpace);
                    for (var i = 0; i < pointList.length; ++i) {
                        coords = pointList[i].split(",");
                        if (coords.length == 2) {
                            coords[2] = null;
                        }
                        if (this.xy) {
                            points.push(new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]));
                        } else {
                            points.push(new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]));
                        }
                    }
                }
            }
            var line = null;
            if (points.length != 0) {
                if (ring) {
                    line = new OpenLayers.Geometry.LinearRing(points);
                } else {
                    line = new OpenLayers.Geometry.LineString(points);
                }
            }
            return line;
        },
        multilinestring: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "LineString");
            var components = [];
            if (nodeList.length > 0) {
                var line;
                for (var i = 0; i < nodeList.length; ++i) {
                    line = this.parseGeometry.linestring.apply(this, [nodeList[i]]);
                    if (line) {
                        components.push(line);
                    }
                }
            }
            return new OpenLayers.Geometry.MultiLineString(components);
        },
        polygon: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "LinearRing");
            var components = [];
            if (nodeList.length > 0) {
                var ring;
                for (var i = 0; i < nodeList.length; ++i) {
                    ring = this.parseGeometry.linestring.apply(this, [nodeList[i], true]);
                    if (ring) {
                        components.push(ring);
                    }
                }
            }
            return new OpenLayers.Geometry.Polygon(components);
        },
        multipolygon: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "Polygon");
            var components = [];
            if (nodeList.length > 0) {
                var polygon;
                for (var i = 0; i < nodeList.length; ++i) {
                    polygon = this.parseGeometry.polygon.apply(this, [nodeList[i]]);
                    if (polygon) {
                        components.push(polygon);
                    }
                }
            }
            return new OpenLayers.Geometry.MultiPolygon(components);
        },
        envelope: function (node) {
            var components = [];
            var coordString;
            var envelope;
            var lpoint = this.getElementsByTagNameNS(node, this.gmlns, "lowerCorner");
            if (lpoint.length > 0) {
                var coords = [];
                if (lpoint.length > 0) {
                    coordString = lpoint[0].firstChild.nodeValue;
                    coordString = coordString.replace(this.regExes.trimSpace, "");
                    coords = coordString.split(this.regExes.splitSpace);
                }
                if (coords.length == 2) {
                    coords[2] = null;
                }
                if (this.xy) {
                    var lowerPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
                } else {
                    var lowerPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);
                }
            }
            var upoint = this.getElementsByTagNameNS(node, this.gmlns, "upperCorner");
            if (upoint.length > 0) {
                var coords = [];
                if (upoint.length > 0) {
                    coordString = upoint[0].firstChild.nodeValue;
                    coordString = coordString.replace(this.regExes.trimSpace, "");
                    coords = coordString.split(this.regExes.splitSpace);
                }
                if (coords.length == 2) {
                    coords[2] = null;
                }
                if (this.xy) {
                    var upperPoint = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
                } else {
                    var upperPoint = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);
                }
            }
            if (lowerPoint && upperPoint) {
                components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
                components.push(new OpenLayers.Geometry.Point(upperPoint.x, lowerPoint.y));
                components.push(new OpenLayers.Geometry.Point(upperPoint.x, upperPoint.y));
                components.push(new OpenLayers.Geometry.Point(lowerPoint.x, upperPoint.y));
                components.push(new OpenLayers.Geometry.Point(lowerPoint.x, lowerPoint.y));
                var ring = new OpenLayers.Geometry.LinearRing(components);
                envelope = new OpenLayers.Geometry.Polygon([ring]);
            }
            return envelope;
        },
        box: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.gmlns, "coordinates");
            var coordString;
            var coords, beginPoint = null,
                endPoint = null;
            if (nodeList.length > 0) {
                coordString = nodeList[0].firstChild.nodeValue;
                coords = coordString.split(" ");
                if (coords.length == 2) {
                    beginPoint = coords[0].split(",");
                    endPoint = coords[1].split(",");
                }
            }
            if (beginPoint !== null && endPoint !== null) {
                return new OpenLayers.Bounds(parseFloat(beginPoint[0]), parseFloat(beginPoint[1]), parseFloat(endPoint[0]), parseFloat(endPoint[1]));
            }
        }
    },
    parseAttributes: function (node) {
        var attributes = {};
        var childNode = node.firstChild;
        var children, i, child, grandchildren, grandchild, name, value;
        while (childNode) {
            if (childNode.nodeType == 1) {
                children = childNode.childNodes;
                for (i = 0; i < children.length; ++i) {
                    child = children[i];
                    if (child.nodeType == 1) {
                        grandchildren = child.childNodes;
                        if (grandchildren.length == 1) {
                            grandchild = grandchildren[0];
                            if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {
                                name = (child.prefix) ? child.nodeName.split(":")[1] : child.nodeName;
                                value = grandchild.nodeValue.replace(this.regExes.trimSpace, "");
                                attributes[name] = value;
                            }
                        } else {
                            attributes[child.nodeName.split(":").pop()] = null;
                        }
                    }
                }
                break;
            }
            childNode = childNode.nextSibling;
        }
        return attributes;
    },
    write: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        var gml = this.createElementNS("http://www.opengis.net/wfs", "wfs:" + this.collectionName);
        for (var i = 0; i < features.length; i++) {
            gml.appendChild(this.createFeatureXML(features[i]));
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [gml]);
    },
    createFeatureXML: function (feature) {
        var geometry = feature.geometry;
        var geometryNode = this.buildGeometryNode(geometry);
        var geomContainer = this.createElementNS(this.featureNS, this.featurePrefix + ":" + this.geometryName);
        geomContainer.appendChild(geometryNode);
        var featureNode = this.createElementNS(this.gmlns, "gml:" + this.featureName);
        var featureContainer = this.createElementNS(this.featureNS, this.featurePrefix + ":" + this.layerName);
        var fid = feature.fid || feature.id;
        featureContainer.setAttribute("fid", fid);
        featureContainer.appendChild(geomContainer);
        for (var attr in feature.attributes) {
            var attrText = this.createTextNode(feature.attributes[attr]);
            var nodename = attr.substring(attr.lastIndexOf(":") + 1);
            var attrContainer = this.createElementNS(this.featureNS, this.featurePrefix + ":" + nodename);
            attrContainer.appendChild(attrText);
            featureContainer.appendChild(attrContainer);
        }
        featureNode.appendChild(featureContainer);
        return featureNode;
    },
    buildGeometryNode: function (geometry) {
        if (this.externalProjection && this.internalProjection) {
            geometry = geometry.clone();
            geometry.transform(this.internalProjection, this.externalProjection);
        }
        var className = geometry.CLASS_NAME;
        var type = className.substring(className.lastIndexOf(".") + 1);
        var builder = this.buildGeometry[type.toLowerCase()];
        return builder.apply(this, [geometry]);
    },
    buildGeometry: {
        point: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:Point");
            gml.appendChild(this.buildCoordinatesNode(geometry));
            return gml;
        },
        multipoint: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:MultiPoint");
            var points = geometry.components;
            var pointMember, pointGeom;
            for (var i = 0; i < points.length; i++) {
                pointMember = this.createElementNS(this.gmlns, "gml:pointMember");
                pointGeom = this.buildGeometry.point.apply(this, [points[i]]);
                pointMember.appendChild(pointGeom);
                gml.appendChild(pointMember);
            }
            return gml;
        },
        linestring: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:LineString");
            gml.appendChild(this.buildCoordinatesNode(geometry));
            return gml;
        },
        multilinestring: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:MultiLineString");
            var lines = geometry.components;
            var lineMember, lineGeom;
            for (var i = 0; i < lines.length; ++i) {
                lineMember = this.createElementNS(this.gmlns, "gml:lineStringMember");
                lineGeom = this.buildGeometry.linestring.apply(this, [lines[i]]);
                lineMember.appendChild(lineGeom);
                gml.appendChild(lineMember);
            }
            return gml;
        },
        linearring: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:LinearRing");
            gml.appendChild(this.buildCoordinatesNode(geometry));
            return gml;
        },
        polygon: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:Polygon");
            var rings = geometry.components;
            var ringMember, ringGeom, type;
            for (var i = 0; i < rings.length; ++i) {
                type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs";
                ringMember = this.createElementNS(this.gmlns, "gml:" + type);
                ringGeom = this.buildGeometry.linearring.apply(this, [rings[i]]);
                ringMember.appendChild(ringGeom);
                gml.appendChild(ringMember);
            }
            return gml;
        },
        multipolygon: function (geometry) {
            var gml = this.createElementNS(this.gmlns, "gml:MultiPolygon");
            var polys = geometry.components;
            var polyMember, polyGeom;
            for (var i = 0; i < polys.length; ++i) {
                polyMember = this.createElementNS(this.gmlns, "gml:polygonMember");
                polyGeom = this.buildGeometry.polygon.apply(this, [polys[i]]);
                polyMember.appendChild(polyGeom);
                gml.appendChild(polyMember);
            }
            return gml;
        },
        bounds: function (bounds) {
            var gml = this.createElementNS(this.gmlns, "gml:Box");
            gml.appendChild(this.buildCoordinatesNode(bounds));
            return gml;
        }
    },
    buildCoordinatesNode: function (geometry) {
        var coordinatesNode = this.createElementNS(this.gmlns, "gml:coordinates");
        coordinatesNode.setAttribute("decimal", ".");
        coordinatesNode.setAttribute("cs", ",");
        coordinatesNode.setAttribute("ts", " ");
        var parts = [];
        if (geometry instanceof OpenLayers.Bounds) {
            parts.push(geometry.left + "," + geometry.bottom);
            parts.push(geometry.right + "," + geometry.top);
        } else {
            var points = (geometry.components) ? geometry.components : [geometry];
            for (var i = 0; i < points.length; i++) {
                parts.push(points[i].x + "," + points[i].y);
            }
        }
        var txtNode = this.createTextNode(parts.join(" "));
        coordinatesNode.appendChild(txtNode);
        return coordinatesNode;
    },
    CLASS_NAME: "OpenLayers.Format.GML"
});
if (!OpenLayers.Format.GML) {
    OpenLayers.Format.GML = {};
}
OpenLayers.Format.GML.Base = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        gml: "http://www.opengis.net/gml",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
        wfs: "http://www.opengis.net/wfs"
    },
    defaultPrefix: "gml",
    schemaLocation: null,
    featureType: null,
    featureNS: null,
    geometryName: "geometry",
    extractAttributes: true,
    srsName: null,
    xy: true,
    geometryTypes: null,
    singleFeatureType: null,
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g),
        featureMember: (/^(.*:)?featureMembers?$/)
    },
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        this.setGeometryTypes();
        if (options && options.featureNS) {
            this.setNamespace("feature", options.featureNS);
        }
        this.singleFeatureType = !options || (typeof options.featureType === "string");
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var features = [];
        this.readNode(data, {
            features: features
        }, true);
        if (features.length == 0) {
            var elements = this.getElementsByTagNameNS(data, this.namespaces.gml, "featureMember");
            if (elements.length) {
                for (var i = 0, len = elements.length; i < len; ++i) {
                    this.readNode(elements[i], {
                        features: features
                    }, true);
                }
            } else {
                var elements = this.getElementsByTagNameNS(data, this.namespaces.gml, "featureMembers");
                if (elements.length) {
                    this.readNode(elements[0], {
                        features: features
                    }, true);
                }
            }
        }
        return features;
    },
    readNode: function (node, obj, first) {
        if (first === true && this.autoConfig === true) {
            this.featureType = null;
            delete this.namespaceAlias[this.featureNS];
            delete this.namespaces["feature"];
            this.featureNS = null;
        }
        if (!this.featureNS && (!(node.prefix in this.namespaces) && node.parentNode.namespaceURI == this.namespaces["gml"] && this.regExes.featureMember.test(node.parentNode.nodeName))) {
            this.featureType = node.nodeName.split(":").pop();
            this.setNamespace("feature", node.namespaceURI);
            this.featureNS = node.namespaceURI;
            this.autoConfig = true;
        }
        return OpenLayers.Format.XML.prototype.readNode.apply(this, [node, obj]);
    },
    readers: {
        "gml": {
            "featureMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "featureMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "name": function (node, obj) {
                obj.name = this.getChildValue(node);
            },
            "boundedBy": function (node, obj) {
                var container = {};
                this.readChildNodes(node, container);
                if (container.components && container.components.length > 0) {
                    obj.bounds = container.components[0];
                }
            },
            "Point": function (node, container) {
                var obj = {
                    points: []
                };
                this.readChildNodes(node, obj);
                if (!container.components) {
                    container.components = [];
                }
                container.components.push(obj.points[0]);
            },
            "coordinates": function (node, obj) {
                var str = this.getChildValue(node).replace(this.regExes.trimSpace, "");
                str = str.replace(this.regExes.trimComma, ",");
                var pointList = str.split(this.regExes.splitSpace);
                var coords;
                var numPoints = pointList.length;
                var points = new Array(numPoints);
                for (var i = 0; i < numPoints; ++i) {
                    coords = pointList[i].split(",");
                    if (this.xy) {
                        points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
                    } else {
                        points[i] = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);
                    }
                }
                obj.points = points;
            },
            "coord": function (node, obj) {
                var coord = {};
                this.readChildNodes(node, coord);
                if (!obj.points) {
                    obj.points = [];
                }
                obj.points.push(new OpenLayers.Geometry.Point(coord.x, coord.y, coord.z));
            },
            "X": function (node, coord) {
                coord.x = this.getChildValue(node);
            },
            "Y": function (node, coord) {
                coord.y = this.getChildValue(node);
            },
            "Z": function (node, coord) {
                coord.z = this.getChildValue(node);
            },
            "MultiPoint": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                container.components = [new OpenLayers.Geometry.MultiPoint(obj.components)];
            },
            "pointMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "LineString": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                if (!container.components) {
                    container.components = [];
                }
                container.components.push(new OpenLayers.Geometry.LineString(obj.points));
            },
            "MultiLineString": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                container.components = [new OpenLayers.Geometry.MultiLineString(obj.components)];
            },
            "lineStringMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Polygon": function (node, container) {
                var obj = {
                    outer: null,
                    inner: []
                };
                this.readChildNodes(node, obj);
                obj.inner.unshift(obj.outer);
                if (!container.components) {
                    container.components = [];
                }
                container.components.push(new OpenLayers.Geometry.Polygon(obj.inner));
            },
            "LinearRing": function (node, obj) {
                var container = {};
                this.readChildNodes(node, container);
                obj.components = [new OpenLayers.Geometry.LinearRing(container.points)];
            },
            "MultiPolygon": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                container.components = [new OpenLayers.Geometry.MultiPolygon(obj.components)];
            },
            "polygonMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "GeometryCollection": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                container.components = [new OpenLayers.Geometry.Collection(obj.components)];
            },
            "geometryMember": function (node, obj) {
                this.readChildNodes(node, obj);
            }
        },
        "feature": {
            "*": function (node, obj) {
                var name;
                var local = node.localName || node.nodeName.split(":").pop();
                if (obj.features) {
                    if (!this.singleFeatureType && (OpenLayers.Util.indexOf(this.featureType, local) !== -1)) {
                        name = "_typeName";
                    } else if (local === this.featureType) {
                        name = "_typeName";
                    }
                } else {
                    if (node.childNodes.length == 0 || (node.childNodes.length == 1 && node.firstChild.nodeType == 3)) {
                        if (this.extractAttributes) {
                            name = "_attribute";
                        }
                    } else {
                        name = "_geometry";
                    }
                }
                if (name) {
                    this.readers.feature[name].apply(this, [node, obj]);
                }
            },
            "_typeName": function (node, obj) {
                var container = {
                    components: [],
                    attributes: {}
                };
                this.readChildNodes(node, container);
                if (container.name) {
                    container.attributes.name = container.name;
                }
                var feature = new OpenLayers.Feature.Vector(container.components[0], container.attributes);
                if (!this.singleFeatureType) {
                    feature.type = node.nodeName.split(":").pop();
                    feature.namespace = node.namespaceURI;
                }
                var fid = node.getAttribute("fid") || this.getAttributeNS(node, this.namespaces["gml"], "id");
                if (fid) {
                    feature.fid = fid;
                }
                if (this.internalProjection && this.externalProjection && feature.geometry) {
                    feature.geometry.transform(this.externalProjection, this.internalProjection);
                }
                if (container.bounds) {
                    feature.bounds = container.bounds;
                }
                obj.features.push(feature);
            },
            "_geometry": function (node, obj) {
                if (!this.geometryName) {
                    this.geometryName = node.nodeName.split(":").pop();
                }
                this.readChildNodes(node, obj);
            },
            "_attribute": function (node, obj) {
                var local = node.localName || node.nodeName.split(":").pop();
                var value = this.getChildValue(node);
                obj.attributes[local] = value;
            }
        },
        "wfs": {
            "FeatureCollection": function (node, obj) {
                this.readChildNodes(node, obj);
            }
        }
    },
    write: function (features) {
        var name;
        if (OpenLayers.Util.isArray(features)) {
            name = "featureMembers";
        } else {
            name = "featureMember";
        }
        var root = this.writeNode("gml:" + name, features);
        this.setAttributeNS(root, this.namespaces["xsi"], "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    writers: {
        "gml": {
            "featureMember": function (feature) {
                var node = this.createElementNSPlus("gml:featureMember");
                this.writeNode("feature:_typeName", feature, node);
                return node;
            },
            "MultiPoint": function (geometry) {
                var node = this.createElementNSPlus("gml:MultiPoint");
                var components = geometry.components || [geometry];
                for (var i = 0, ii = components.length; i < ii; ++i) {
                    this.writeNode("pointMember", components[i], node);
                }
                return node;
            },
            "pointMember": function (geometry) {
                var node = this.createElementNSPlus("gml:pointMember");
                this.writeNode("Point", geometry, node);
                return node;
            },
            "MultiLineString": function (geometry) {
                var node = this.createElementNSPlus("gml:MultiLineString");
                var components = geometry.components || [geometry];
                for (var i = 0, ii = components.length; i < ii; ++i) {
                    this.writeNode("lineStringMember", components[i], node);
                }
                return node;
            },
            "lineStringMember": function (geometry) {
                var node = this.createElementNSPlus("gml:lineStringMember");
                this.writeNode("LineString", geometry, node);
                return node;
            },
            "MultiPolygon": function (geometry) {
                var node = this.createElementNSPlus("gml:MultiPolygon");
                var components = geometry.components || [geometry];
                for (var i = 0, ii = components.length; i < ii; ++i) {
                    this.writeNode("polygonMember", components[i], node);
                }
                return node;
            },
            "polygonMember": function (geometry) {
                var node = this.createElementNSPlus("gml:polygonMember");
                this.writeNode("Polygon", geometry, node);
                return node;
            },
            "GeometryCollection": function (geometry) {
                var node = this.createElementNSPlus("gml:GeometryCollection");
                for (var i = 0, len = geometry.components.length; i < len; ++i) {
                    this.writeNode("geometryMember", geometry.components[i], node);
                }
                return node;
            },
            "geometryMember": function (geometry) {
                var node = this.createElementNSPlus("gml:geometryMember");
                var child = this.writeNode("feature:_geometry", geometry);
                node.appendChild(child.firstChild);
                return node;
            }
        },
        "feature": {
            "_typeName": function (feature) {
                var node = this.createElementNSPlus("feature:" + this.featureType, {
                    attributes: {
                        fid: feature.fid
                    }
                });
                if (feature.geometry) {
                    this.writeNode("feature:_geometry", feature.geometry, node);
                }
                for (var name in feature.attributes) {
                    var value = feature.attributes[name];
                    if (value != null) {
                        this.writeNode("feature:_attribute", {
                            name: name,
                            value: value
                        }, node);
                    }
                }
                return node;
            },
            "_geometry": function (geometry) {
                if (this.externalProjection && this.internalProjection) {
                    geometry = geometry.clone().transform(this.internalProjection, this.externalProjection);
                }
                var node = this.createElementNSPlus("feature:" + this.geometryName);
                var type = this.geometryTypes[geometry.CLASS_NAME];
                var child = this.writeNode("gml:" + type, geometry, node);
                if (this.srsName) {
                    child.setAttribute("srsName", this.srsName);
                }
                return node;
            },
            "_attribute": function (obj) {
                return this.createElementNSPlus("feature:" + obj.name, {
                    value: obj.value
                });
            }
        },
        "wfs": {
            "FeatureCollection": function (features) {
                var node = this.createElementNSPlus("wfs:FeatureCollection");
                for (var i = 0, len = features.length; i < len; ++i) {
                    this.writeNode("gml:featureMember", features[i], node);
                }
                return node;
            }
        }
    },
    setGeometryTypes: function () {
        this.geometryTypes = {
            "OpenLayers.Geometry.Point": "Point",
            "OpenLayers.Geometry.MultiPoint": "MultiPoint",
            "OpenLayers.Geometry.LineString": "LineString",
            "OpenLayers.Geometry.MultiLineString": "MultiLineString",
            "OpenLayers.Geometry.Polygon": "Polygon",
            "OpenLayers.Geometry.MultiPolygon": "MultiPolygon",
            "OpenLayers.Geometry.Collection": "GeometryCollection"
        };
    },
    CLASS_NAME: "OpenLayers.Format.GML.Base"
});
OpenLayers.Format.GML.v3 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
    schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd",
    curve: false,
    multiCurve: true,
    surface: false,
    multiSurface: true,
    initialize: function (options) {
        OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "gml": OpenLayers.Util.applyDefaults({
            "featureMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Curve": function (node, container) {
                var obj = {
                    points: []
                };
                this.readChildNodes(node, obj);
                if (!container.components) {
                    container.components = [];
                }
                container.components.push(new OpenLayers.Geometry.LineString(obj.points));
            },
            "segments": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "LineStringSegment": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                if (obj.points) {
                    Array.prototype.push.apply(container.points, obj.points);
                }
            },
            "pos": function (node, obj) {
                var str = this.getChildValue(node).replace(this.regExes.trimSpace, "");
                var coords = str.split(this.regExes.splitSpace);
                var point;
                if (this.xy) {
                    point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
                } else {
                    point = new OpenLayers.Geometry.Point(coords[1], coords[0], coords[2]);
                }
                obj.points = [point];
            },
            "posList": function (node, obj) {
                var str = this.getChildValue(node).replace(this.regExes.trimSpace, "");
                var coords = str.split(this.regExes.splitSpace);
                var dim = parseInt(node.getAttribute("dimension")) || 2;
                var j, x, y, z;
                var numPoints = coords.length / dim;
                var points = new Array(numPoints);
                for (var i = 0, len = coords.length; i < len; i += dim) {
                    x = coords[i];
                    y = coords[i + 1];
                    z = (dim == 2) ? undefined : coords[i + 2];
                    if (this.xy) {
                        points[i / dim] = new OpenLayers.Geometry.Point(x, y, z);
                    } else {
                        points[i / dim] = new OpenLayers.Geometry.Point(y, x, z);
                    }
                }
                obj.points = points;
            },
            "Surface": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "patches": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "PolygonPatch": function (node, obj) {
                this.readers.gml.Polygon.apply(this, [node, obj]);
            },
            "exterior": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                container.outer = obj.components[0];
            },
            "interior": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                container.inner.push(obj.components[0]);
            },
            "MultiCurve": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                if (obj.components.length > 0) {
                    container.components = [new OpenLayers.Geometry.MultiLineString(obj.components)];
                }
            },
            "curveMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "MultiSurface": function (node, container) {
                var obj = {
                    components: []
                };
                this.readChildNodes(node, obj);
                if (obj.components.length > 0) {
                    container.components = [new OpenLayers.Geometry.MultiPolygon(obj.components)];
                }
            },
            "surfaceMember": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "surfaceMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "pointMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "lineStringMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "polygonMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "geometryMembers": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Envelope": function (node, container) {
                var obj = {
                    points: new Array(2)
                };
                this.readChildNodes(node, obj);
                if (!container.components) {
                    container.components = [];
                }
                var min = obj.points[0];
                var max = obj.points[1];
                container.components.push(new OpenLayers.Bounds(min.x, min.y, max.x, max.y));
            },
            "lowerCorner": function (node, container) {
                var obj = {};
                this.readers.gml.pos.apply(this, [node, obj]);
                container.points[0] = obj.points[0];
            },
            "upperCorner": function (node, container) {
                var obj = {};
                this.readers.gml.pos.apply(this, [node, obj]);
                container.points[1] = obj.points[0];
            }
        }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
        "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
        "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
    },
    write: function (features) {
        var name;
        if (OpenLayers.Util.isArray(features)) {
            name = "featureMembers";
        } else {
            name = "featureMember";
        }
        var root = this.writeNode("gml:" + name, features);
        this.setAttributeNS(root, this.namespaces["xsi"], "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    writers: {
        "gml": OpenLayers.Util.applyDefaults({
            "featureMembers": function (features) {
                var node = this.createElementNSPlus("gml:featureMembers");
                for (var i = 0, len = features.length; i < len; ++i) {
                    this.writeNode("feature:_typeName", features[i], node);
                }
                return node;
            },
            "Point": function (geometry) {
                var node = this.createElementNSPlus("gml:Point");
                this.writeNode("pos", geometry, node);
                return node;
            },
            "pos": function (point) {
                var pos = (this.xy) ? (point.x + " " + point.y) : (point.y + " " + point.x);
                return this.createElementNSPlus("gml:pos", {
                    value: pos
                });
            },
            "LineString": function (geometry) {
                var node = this.createElementNSPlus("gml:LineString");
                this.writeNode("posList", geometry.components, node);
                return node;
            },
            "Curve": function (geometry) {
                var node = this.createElementNSPlus("gml:Curve");
                this.writeNode("segments", geometry, node);
                return node;
            },
            "segments": function (geometry) {
                var node = this.createElementNSPlus("gml:segments");
                this.writeNode("LineStringSegment", geometry, node);
                return node;
            },
            "LineStringSegment": function (geometry) {
                var node = this.createElementNSPlus("gml:LineStringSegment");
                this.writeNode("posList", geometry.components, node);
                return node;
            },
            "posList": function (points) {
                var len = points.length;
                var parts = new Array(len);
                var point;
                for (var i = 0; i < len; ++i) {
                    point = points[i];
                    if (this.xy) {
                        parts[i] = point.x + " " + point.y;
                    } else {
                        parts[i] = point.y + " " + point.x;
                    }
                }
                return this.createElementNSPlus("gml:posList", {
                    value: parts.join(" ")
                });
            },
            "Surface": function (geometry) {
                var node = this.createElementNSPlus("gml:Surface");
                this.writeNode("patches", geometry, node);
                return node;
            },
            "patches": function (geometry) {
                var node = this.createElementNSPlus("gml:patches");
                this.writeNode("PolygonPatch", geometry, node);
                return node;
            },
            "PolygonPatch": function (geometry) {
                var node = this.createElementNSPlus("gml:PolygonPatch", {
                    attributes: {
                        interpolation: "planar"
                    }
                });
                this.writeNode("exterior", geometry.components[0], node);
                for (var i = 1, len = geometry.components.length; i < len; ++i) {
                    this.writeNode("interior", geometry.components[i], node);
                }
                return node;
            },
            "Polygon": function (geometry) {
                var node = this.createElementNSPlus("gml:Polygon");
                this.writeNode("exterior", geometry.components[0], node);
                for (var i = 1, len = geometry.components.length; i < len; ++i) {
                    this.writeNode("interior", geometry.components[i], node);
                }
                return node;
            },
            "exterior": function (ring) {
                var node = this.createElementNSPlus("gml:exterior");
                this.writeNode("LinearRing", ring, node);
                return node;
            },
            "interior": function (ring) {
                var node = this.createElementNSPlus("gml:interior");
                this.writeNode("LinearRing", ring, node);
                return node;
            },
            "LinearRing": function (ring) {
                var node = this.createElementNSPlus("gml:LinearRing");
                this.writeNode("posList", ring.components, node);
                return node;
            },
            "MultiCurve": function (geometry) {
                var node = this.createElementNSPlus("gml:MultiCurve");
                var components = geometry.components || [geometry];
                for (var i = 0, len = components.length; i < len; ++i) {
                    this.writeNode("curveMember", components[i], node);
                }
                return node;
            },
            "curveMember": function (geometry) {
                var node = this.createElementNSPlus("gml:curveMember");
                if (this.curve) {
                    this.writeNode("Curve", geometry, node);
                } else {
                    this.writeNode("LineString", geometry, node);
                }
                return node;
            },
            "MultiSurface": function (geometry) {
                var node = this.createElementNSPlus("gml:MultiSurface");
                var components = geometry.components || [geometry];
                for (var i = 0, len = components.length; i < len; ++i) {
                    this.writeNode("surfaceMember", components[i], node);
                }
                return node;
            },
            "surfaceMember": function (polygon) {
                var node = this.createElementNSPlus("gml:surfaceMember");
                if (this.surface) {
                    this.writeNode("Surface", polygon, node);
                } else {
                    this.writeNode("Polygon", polygon, node);
                }
                return node;
            },
            "Envelope": function (bounds) {
                var node = this.createElementNSPlus("gml:Envelope");
                this.writeNode("lowerCorner", bounds, node);
                this.writeNode("upperCorner", bounds, node);
                if (this.srsName) {
                    node.setAttribute("srsName", this.srsName);
                }
                return node;
            },
            "lowerCorner": function (bounds) {
                var pos = (this.xy) ? (bounds.left + " " + bounds.bottom) : (bounds.bottom + " " + bounds.left);
                return this.createElementNSPlus("gml:lowerCorner", {
                    value: pos
                });
            },
            "upperCorner": function (bounds) {
                var pos = (this.xy) ? (bounds.right + " " + bounds.top) : (bounds.top + " " + bounds.right);
                return this.createElementNSPlus("gml:upperCorner", {
                    value: pos
                });
            }
        }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
        "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
        "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
    },
    setGeometryTypes: function () {
        this.geometryTypes = {
            "OpenLayers.Geometry.Point": "Point",
            "OpenLayers.Geometry.MultiPoint": "MultiPoint",
            "OpenLayers.Geometry.LineString": (this.curve === true) ? "Curve" : "LineString",
            "OpenLayers.Geometry.MultiLineString": (this.multiCurve === false) ? "MultiLineString" : "MultiCurve",
            "OpenLayers.Geometry.Polygon": (this.surface === true) ? "Surface" : "Polygon",
            "OpenLayers.Geometry.MultiPolygon": (this.multiSurface === false) ? "MultiPolygon" : "MultiSurface",
            "OpenLayers.Geometry.Collection": "GeometryCollection"
        };
    },
    CLASS_NAME: "OpenLayers.Format.GML.v3"
});
OpenLayers.Format.Filter.v1_1_0 = OpenLayers.Class(OpenLayers.Format.GML.v3, OpenLayers.Format.Filter.v1, {
    VERSION: "1.1.0",
    schemaLocation: "http://www.opengis.net/ogc/filter/1.1.0/filter.xsd",
    initialize: function (options) {
        OpenLayers.Format.GML.v3.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "ogc": OpenLayers.Util.applyDefaults({
            "PropertyIsEqualTo": function (node, obj) {
                var matchCase = node.getAttribute("matchCase");
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
                    matchCase: !(matchCase === "false" || matchCase === "0")
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsNotEqualTo": function (node, obj) {
                var matchCase = node.getAttribute("matchCase");
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
                    matchCase: !(matchCase === "false" || matchCase === "0")
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsLike": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.LIKE
                });
                this.readChildNodes(node, filter);
                var wildCard = node.getAttribute("wildCard");
                var singleChar = node.getAttribute("singleChar");
                var esc = node.getAttribute("escapeChar");
                filter.value2regex(wildCard, singleChar, esc);
                obj.filters.push(filter);
            }
        }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
        "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
        "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"]
    },
    writers: {
        "ogc": OpenLayers.Util.applyDefaults({
            "PropertyIsEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", {
                    attributes: {
                        matchCase: filter.matchCase
                    }
                });
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsNotEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", {
                    attributes: {
                        matchCase: filter.matchCase
                    }
                });
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsLike": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
                    attributes: {
                        matchCase: filter.matchCase,
                        wildCard: "*",
                        singleChar: ".",
                        escapeChar: "!"
                    }
                });
                this.writeNode("PropertyName", filter, node);
                this.writeNode("Literal", filter.regex2value(), node);
                return node;
            },
            "BBOX": function (filter) {
                var node = this.createElementNSPlus("ogc:BBOX");
                filter.property && this.writeNode("PropertyName", filter, node);
                var box = this.writeNode("gml:Envelope", filter.value);
                if (filter.projection) {
                    box.setAttribute("srsName", filter.projection);
                }
                node.appendChild(box);
                return node;
            },
            "SortBy": function (sortProperties) {
                var node = this.createElementNSPlus("ogc:SortBy");
                for (var i = 0, l = sortProperties.length; i < l; i++) {
                    this.writeNode("ogc:SortProperty", sortProperties[i], node);
                }
                return node;
            },
            "SortProperty": function (sortProperty) {
                var node = this.createElementNSPlus("ogc:SortProperty");
                this.writeNode("ogc:PropertyName", sortProperty, node);
                this.writeNode("ogc:SortOrder", (sortProperty.order == 'DESC') ? 'DESC' : 'ASC', node);
                return node;
            },
            "SortOrder": function (value) {
                var node = this.createElementNSPlus("ogc:SortOrder", {
                    value: value
                });
                return node;
            }
        }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
        "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
        "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"]
    },
    writeSpatial: function (filter, name) {
        var node = this.createElementNSPlus("ogc:" + name);
        this.writeNode("PropertyName", filter, node);
        if (filter.value instanceof OpenLayers.Filter.Function) {
            this.writeNode("Function", filter.value, node);
        } else {
            var child;
            if (filter.value instanceof OpenLayers.Geometry) {
                child = this.writeNode("feature:_geometry", filter.value).firstChild;
            } else {
                child = this.writeNode("gml:Envelope", filter.value);
            }
            if (filter.projection) {
                child.setAttribute("srsName", filter.projection);
            }
            node.appendChild(child);
        }
        return node;
    },
    CLASS_NAME: "OpenLayers.Format.Filter.v1_1_0"
});
OpenLayers.Format.OWSCommon = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    getVersion: function (root, options) {
        var version = this.version;
        if (!version) {
            var uri = root.getAttribute("xmlns:ows");
            if (uri && uri.substring(uri.lastIndexOf("/") + 1) === "1.1") {
                version = "1.1.0";
            }
            if (!version) {
                version = this.defaultVersion;
            }
        }
        return version;
    },
    CLASS_NAME: "OpenLayers.Format.OWSCommon"
});
OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    read: function (data, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var ows = {};
        this.readChildNodes(data, ows);
        return ows;
    },
    readers: {
        "ows": {
            "Exception": function (node, exceptionReport) {
                var exception = {
                    code: node.getAttribute('exceptionCode'),
                    locator: node.getAttribute('locator'),
                    texts: []
                };
                exceptionReport.exceptions.push(exception);
                this.readChildNodes(node, exception);
            },
            "ExceptionText": function (node, exception) {
                var text = this.getChildValue(node);
                exception.texts.push(text);
            },
            "ServiceIdentification": function (node, obj) {
                obj.serviceIdentification = {};
                this.readChildNodes(node, obj.serviceIdentification);
            },
            "Title": function (node, obj) {
                obj.title = this.getChildValue(node);
            },
            "Abstract": function (node, serviceIdentification) {
                serviceIdentification["abstract"] = this.getChildValue(node);
            },
            "Keywords": function (node, serviceIdentification) {
                serviceIdentification.keywords = {};
                this.readChildNodes(node, serviceIdentification.keywords);
            },
            "Keyword": function (node, keywords) {
                keywords[this.getChildValue(node)] = true;
            },
            "ServiceType": function (node, serviceIdentification) {
                serviceIdentification.serviceType = {
                    codeSpace: node.getAttribute('codeSpace'),
                    value: this.getChildValue(node)
                };
            },
            "ServiceTypeVersion": function (node, serviceIdentification) {
                serviceIdentification.serviceTypeVersion = this.getChildValue(node);
            },
            "Fees": function (node, serviceIdentification) {
                serviceIdentification.fees = this.getChildValue(node);
            },
            "AccessConstraints": function (node, serviceIdentification) {
                serviceIdentification.accessConstraints = this.getChildValue(node);
            },
            "ServiceProvider": function (node, obj) {
                obj.serviceProvider = {};
                this.readChildNodes(node, obj.serviceProvider);
            },
            "ProviderName": function (node, serviceProvider) {
                serviceProvider.providerName = this.getChildValue(node);
            },
            "ProviderSite": function (node, serviceProvider) {
                serviceProvider.providerSite = this.getAttributeNS(node, this.namespaces.xlink, "href");
            },
            "ServiceContact": function (node, serviceProvider) {
                serviceProvider.serviceContact = {};
                this.readChildNodes(node, serviceProvider.serviceContact);
            },
            "IndividualName": function (node, serviceContact) {
                serviceContact.individualName = this.getChildValue(node);
            },
            "PositionName": function (node, serviceContact) {
                serviceContact.positionName = this.getChildValue(node);
            },
            "ContactInfo": function (node, serviceContact) {
                serviceContact.contactInfo = {};
                this.readChildNodes(node, serviceContact.contactInfo);
            },
            "Phone": function (node, contactInfo) {
                contactInfo.phone = {};
                this.readChildNodes(node, contactInfo.phone);
            },
            "Voice": function (node, phone) {
                phone.voice = this.getChildValue(node);
            },
            "Address": function (node, contactInfo) {
                contactInfo.address = {};
                this.readChildNodes(node, contactInfo.address);
            },
            "DeliveryPoint": function (node, address) {
                address.deliveryPoint = this.getChildValue(node);
            },
            "City": function (node, address) {
                address.city = this.getChildValue(node);
            },
            "AdministrativeArea": function (node, address) {
                address.administrativeArea = this.getChildValue(node);
            },
            "PostalCode": function (node, address) {
                address.postalCode = this.getChildValue(node);
            },
            "Country": function (node, address) {
                address.country = this.getChildValue(node);
            },
            "ElectronicMailAddress": function (node, address) {
                address.electronicMailAddress = this.getChildValue(node);
            },
            "Role": function (node, serviceContact) {
                serviceContact.role = this.getChildValue(node);
            },
            "OperationsMetadata": function (node, obj) {
                obj.operationsMetadata = {};
                this.readChildNodes(node, obj.operationsMetadata);
            },
            "Operation": function (node, operationsMetadata) {
                var name = node.getAttribute("name");
                operationsMetadata[name] = {};
                this.readChildNodes(node, operationsMetadata[name]);
            },
            "DCP": function (node, operation) {
                operation.dcp = {};
                this.readChildNodes(node, operation.dcp);
            },
            "HTTP": function (node, dcp) {
                dcp.http = {};
                this.readChildNodes(node, dcp.http);
            },
            "Get": function (node, http) {
                http.get = this.getAttributeNS(node, this.namespaces.xlink, "href");
            },
            "Post": function (node, http) {
                http.post = this.getAttributeNS(node, this.namespaces.xlink, "href");
            },
            "Parameter": function (node, operation) {
                if (!operation.parameters) {
                    operation.parameters = {};
                }
                var name = node.getAttribute("name");
                operation.parameters[name] = {};
                this.readChildNodes(node, operation.parameters[name]);
            },
            "Value": function (node, allowedValues) {
                allowedValues[this.getChildValue(node)] = true;
            },
            "OutputFormat": function (node, obj) {
                obj.formats.push({
                    value: this.getChildValue(node)
                });
                this.readChildNodes(node, obj);
            },
            "WGS84BoundingBox": function (node, obj) {
                var boundingBox = {};
                boundingBox.crs = node.getAttribute("crs");
                if (obj.BoundingBox) {
                    obj.BoundingBox.push(boundingBox);
                } else {
                    obj.projection = boundingBox.crs;
                    boundingBox = obj;
                }
                this.readChildNodes(node, boundingBox);
            },
            "BoundingBox": function (node, obj) {
                this.readers['ows']['WGS84BoundingBox'].apply(this, [node, obj]);
            },
            "LowerCorner": function (node, obj) {
                var str = this.getChildValue(node).replace(this.regExes.trimSpace, "");
                str = str.replace(this.regExes.trimComma, ",");
                var pointList = str.split(this.regExes.splitSpace);
                obj.left = pointList[0];
                obj.bottom = pointList[1];
            },
            "UpperCorner": function (node, obj) {
                var str = this.getChildValue(node).replace(this.regExes.trimSpace, "");
                str = str.replace(this.regExes.trimComma, ",");
                var pointList = str.split(this.regExes.splitSpace);
                obj.right = pointList[0];
                obj.top = pointList[1];
                obj.bounds = new OpenLayers.Bounds(obj.left, obj.bottom, obj.right, obj.top);
                delete obj.left;
                delete obj.bottom;
                delete obj.right;
                delete obj.top;
            },
            "Language": function (node, obj) {
                obj.language = this.getChildValue(node);
            }
        }
    },
    writers: {
        "ows": {
            "BoundingBox": function (options) {
                var node = this.createElementNSPlus("ows:BoundingBox", {
                    attributes: {
                        crs: options.projection
                    }
                });
                this.writeNode("ows:LowerCorner", options, node);
                this.writeNode("ows:UpperCorner", options, node);
                return node;
            },
            "LowerCorner": function (options) {
                var node = this.createElementNSPlus("ows:LowerCorner", {
                    value: options.bounds.left + " " + options.bounds.bottom
                });
                return node;
            },
            "UpperCorner": function (options) {
                var node = this.createElementNSPlus("ows:UpperCorner", {
                    value: options.bounds.right + " " + options.bounds.top
                });
                return node;
            },
            "Identifier": function (identifier) {
                var node = this.createElementNSPlus("ows:Identifier", {
                    value: identifier
                });
                return node;
            },
            "Title": function (title) {
                var node = this.createElementNSPlus("ows:Title", {
                    value: title
                });
                return node;
            },
            "Abstract": function (abstractValue) {
                var node = this.createElementNSPlus("ows:Abstract", {
                    value: abstractValue
                });
                return node;
            },
            "OutputFormat": function (format) {
                var node = this.createElementNSPlus("ows:OutputFormat", {
                    value: format
                });
                return node;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.OWSCommon.v1"
});
OpenLayers.Format.OWSCommon.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {
    namespaces: {
        ows: "http://www.opengis.net/ows",
        xlink: "http://www.w3.org/1999/xlink"
    },
    readers: {
        "ows": OpenLayers.Util.applyDefaults({
            "ExceptionReport": function (node, obj) {
                obj.success = false;
                obj.exceptionReport = {
                    version: node.getAttribute('version'),
                    language: node.getAttribute('language'),
                    exceptions: []
                };
                this.readChildNodes(node, obj.exceptionReport);
            }
        }, OpenLayers.Format.OWSCommon.v1.prototype.readers.ows)
    },
    writers: {
        "ows": OpenLayers.Format.OWSCommon.v1.prototype.writers.ows
    },
    CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_0_0"
});
OpenLayers.Format.WFST.v1_1_0 = OpenLayers.Class(OpenLayers.Format.Filter.v1_1_0, OpenLayers.Format.WFST.v1, {
    version: "1.1.0",
    schemaLocations: {
        "wfs": "http://schemas.opengis.net/wfs/1.1.0/wfs.xsd"
    },
    initialize: function (options) {
        OpenLayers.Format.Filter.v1_1_0.prototype.initialize.apply(this, [options]);
        OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
    },
    readNode: function (node, obj, first) {
        return OpenLayers.Format.GML.v3.prototype.readNode.apply(this, [node, obj]);
    },
    readers: {
        "wfs": OpenLayers.Util.applyDefaults({
            "FeatureCollection": function (node, obj) {
                obj.numberOfFeatures = parseInt(node.getAttribute("numberOfFeatures"));
                OpenLayers.Format.WFST.v1.prototype.readers["wfs"]["FeatureCollection"].apply(this, arguments);
            },
            "TransactionResponse": function (node, obj) {
                obj.insertIds = [];
                obj.success = false;
                this.readChildNodes(node, obj);
            },
            "TransactionSummary": function (node, obj) {
                obj.success = true;
            },
            "InsertResults": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Feature": function (node, container) {
                var obj = {
                    fids: []
                };
                this.readChildNodes(node, obj);
                container.insertIds.push(obj.fids[0]);
            }
        }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
        "gml": OpenLayers.Format.GML.v3.prototype.readers["gml"],
        "feature": OpenLayers.Format.GML.v3.prototype.readers["feature"],
        "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.readers["ogc"],
        "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]
    },
    writers: {
        "wfs": OpenLayers.Util.applyDefaults({
            "GetFeature": function (options) {
                var node = OpenLayers.Format.WFST.v1.prototype.writers["wfs"]["GetFeature"].apply(this, arguments);
                options && this.setAttributes(node, {
                    resultType: options.resultType,
                    startIndex: options.startIndex,
                    count: options.count
                });
                return node;
            },
            "Query": function (options) {
                options = OpenLayers.Util.extend({
                    featureNS: this.featureNS,
                    featurePrefix: this.featurePrefix,
                    featureType: this.featureType,
                    srsName: this.srsName
                }, options);
                var prefix = options.featurePrefix;
                var node = this.createElementNSPlus("wfs:Query", {
                    attributes: {
                        typeName: (prefix ? prefix + ":" : "") + options.featureType,
                        srsName: options.srsName
                    }
                });
                if (options.featureNS) {
                    node.setAttribute("xmlns:" + prefix, options.featureNS);
                }
                if (options.propertyNames) {
                    for (var i = 0, len = options.propertyNames.length; i < len; i++) {
                        this.writeNode("wfs:PropertyName", {
                            property: options.propertyNames[i]
                        }, node);
                    }
                }
                if (options.filter) {
                    this.setFilterProperty(options.filter);
                    this.writeNode("ogc:Filter", options.filter, node);
                }
                return node;
            },
            "PropertyName": function (obj) {
                return this.createElementNSPlus("wfs:PropertyName", {
                    value: obj.property
                });
            }
        }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
        "gml": OpenLayers.Format.GML.v3.prototype.writers["gml"],
        "feature": OpenLayers.Format.GML.v3.prototype.writers["feature"],
        "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"]
    },
    CLASS_NAME: "OpenLayers.Format.WFST.v1_1_0"
});
OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, {
    ignoreExtraDims: false,
    read: function (json, type, filter) {
        type = (type) ? type : "FeatureCollection";
        var results = null;
        var obj = null;
        if (typeof json == "string") {
            obj = OpenLayers.Format.JSON.prototype.read.apply(this, [json, filter]);
        } else {
            obj = json;
        }
        if (!obj) {
            OpenLayers.Console.error("Bad JSON: " + json);
        } else if (typeof (obj.type) != "string") {
            OpenLayers.Console.error("Bad GeoJSON - no type: " + json);
        } else if (this.isValidType(obj, type)) {
            switch (type) {
            case "Geometry":
                try {
                    results = this.parseGeometry(obj);
                } catch (err) {
                    OpenLayers.Console.error(err);
                }
                break;
            case "Feature":
                try {
                    results = this.parseFeature(obj);
                    results.type = "Feature";
                } catch (err) {
                    OpenLayers.Console.error(err);
                }
                break;
            case "FeatureCollection":
                results = [];
                switch (obj.type) {
                case "Feature":
                    try {
                        results.push(this.parseFeature(obj));
                    } catch (err) {
                        results = null;
                        OpenLayers.Console.error(err);
                    }
                    break;
                case "FeatureCollection":
                    for (var i = 0, len = obj.features.length; i < len; ++i) {
                        try {
                            results.push(this.parseFeature(obj.features[i]));
                        } catch (err) {
                            results = null;
                            OpenLayers.Console.error(err);
                        }
                    }
                    break;
                default:
                    try {
                        var geom = this.parseGeometry(obj);
                        results.push(new OpenLayers.Feature.Vector(geom));
                    } catch (err) {
                        results = null;
                        OpenLayers.Console.error(err);
                    }
                }
                break;
            }
        }
        return results;
    },
    isValidType: function (obj, type) {
        var valid = false;
        switch (type) {
        case "Geometry":
            if (OpenLayers.Util.indexOf(["Point", "MultiPoint", "LineString", "MultiLineString", "Polygon", "MultiPolygon", "Box", "GeometryCollection"], obj.type) == -1) {
                OpenLayers.Console.error("Unsupported geometry type: " + obj.type);
            } else {
                valid = true;
            }
            break;
        case "FeatureCollection":
            valid = true;
            break;
        default:
            if (obj.type == type) {
                valid = true;
            } else {
                OpenLayers.Console.error("Cannot convert types from " + obj.type + " to " + type);
            }
        }
        return valid;
    },
    parseFeature: function (obj) {
        var feature, geometry, attributes, bbox;
        attributes = (obj.properties) ? obj.properties : {};
        bbox = (obj.geometry && obj.geometry.bbox) || obj.bbox;
        try {
            geometry = this.parseGeometry(obj.geometry);
        } catch (err) {
            throw err;
        }
        feature = new OpenLayers.Feature.Vector(geometry, attributes);
        if (bbox) {
            feature.bounds = OpenLayers.Bounds.fromArray(bbox);
        }
        if (obj.id) {
            feature.fid = obj.id;
        }
        return feature;
    },
    parseGeometry: function (obj) {
        if (obj == null) {
            return null;
        }
        var geometry, collection = false;
        if (obj.type == "GeometryCollection") {
            if (!(OpenLayers.Util.isArray(obj.geometries))) {
                throw "GeometryCollection must have geometries array: " + obj;
            }
            var numGeom = obj.geometries.length;
            var components = new Array(numGeom);
            for (var i = 0; i < numGeom; ++i) {
                components[i] = this.parseGeometry.apply(this, [obj.geometries[i]]);
            }
            geometry = new OpenLayers.Geometry.Collection(components);
            collection = true;
        } else {
            if (!(OpenLayers.Util.isArray(obj.coordinates))) {
                throw "Geometry must have coordinates array: " + obj;
            }
            if (!this.parseCoords[obj.type.toLowerCase()]) {
                throw "Unsupported geometry type: " + obj.type;
            }
            try {
                geometry = this.parseCoords[obj.type.toLowerCase()].apply(this, [obj.coordinates]);
            } catch (err) {
                throw err;
            }
        }
        if (this.internalProjection && this.externalProjection && !collection) {
            geometry.transform(this.externalProjection, this.internalProjection);
        }
        return geometry;
    },
    parseCoords: {
        "point": function (array) {
            if (this.ignoreExtraDims == false && array.length != 2) {
                throw "Only 2D points are supported: " + array;
            }
            return new OpenLayers.Geometry.Point(array[0], array[1]);
        },
        "multipoint": function (array) {
            var points = [];
            var p = null;
            for (var i = 0, len = array.length; i < len; ++i) {
                try {
                    p = this.parseCoords["point"].apply(this, [array[i]]);
                } catch (err) {
                    throw err;
                }
                points.push(p);
            }
            return new OpenLayers.Geometry.MultiPoint(points);
        },
        "linestring": function (array) {
            var points = [];
            var p = null;
            for (var i = 0, len = array.length; i < len; ++i) {
                try {
                    p = this.parseCoords["point"].apply(this, [array[i]]);
                } catch (err) {
                    throw err;
                }
                points.push(p);
            }
            return new OpenLayers.Geometry.LineString(points);
        },
        "multilinestring": function (array) {
            var lines = [];
            var l = null;
            for (var i = 0, len = array.length; i < len; ++i) {
                try {
                    l = this.parseCoords["linestring"].apply(this, [array[i]]);
                } catch (err) {
                    throw err;
                }
                lines.push(l);
            }
            return new OpenLayers.Geometry.MultiLineString(lines);
        },
        "polygon": function (array) {
            var rings = [];
            var r, l;
            for (var i = 0, len = array.length; i < len; ++i) {
                try {
                    l = this.parseCoords["linestring"].apply(this, [array[i]]);
                } catch (err) {
                    throw err;
                }
                r = new OpenLayers.Geometry.LinearRing(l.components);
                rings.push(r);
            }
            return new OpenLayers.Geometry.Polygon(rings);
        },
        "multipolygon": function (array) {
            var polys = [];
            var p = null;
            for (var i = 0, len = array.length; i < len; ++i) {
                try {
                    p = this.parseCoords["polygon"].apply(this, [array[i]]);
                } catch (err) {
                    throw err;
                }
                polys.push(p);
            }
            return new OpenLayers.Geometry.MultiPolygon(polys);
        },
        "box": function (array) {
            if (array.length != 2) {
                throw "GeoJSON box coordinates must have 2 elements";
            }
            return new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([new OpenLayers.Geometry.Point(array[0][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[0][1]), new OpenLayers.Geometry.Point(array[1][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[1][1]), new OpenLayers.Geometry.Point(array[0][0], array[0][1])])]);
        }
    },
    write: function (obj, pretty) {
        var geojson = {
            "type": null
        };
        if (OpenLayers.Util.isArray(obj)) {
            geojson.type = "FeatureCollection";
            var numFeatures = obj.length;
            geojson.features = new Array(numFeatures);
            for (var i = 0; i < numFeatures; ++i) {
                var element = obj[i];
                if (!element instanceof OpenLayers.Feature.Vector) {
                    var msg = "FeatureCollection only supports collections " + "of features: " + element;
                    throw msg;
                }
                geojson.features[i] = this.extract.feature.apply(this, [element]);
            }
        } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) {
            geojson = this.extract.geometry.apply(this, [obj]);
        } else if (obj instanceof OpenLayers.Feature.Vector) {
            geojson = this.extract.feature.apply(this, [obj]);
            if (obj.layer && obj.layer.projection) {
                geojson.crs = this.createCRSObject(obj);
            }
        }
        return OpenLayers.Format.JSON.prototype.write.apply(this, [geojson, pretty]);
    },
    createCRSObject: function (object) {
        var proj = object.layer.projection.toString();
        var crs = {};
        if (proj.match(/epsg:/i)) {
            var code = parseInt(proj.substring(proj.indexOf(":") + 1));
            if (code == 4326) {
                crs = {
                    "type": "name",
                    "properties": {
                        "name": "urn:ogc:def:crs:OGC:1.3:CRS84"
                    }
                };
            } else {
                crs = {
                    "type": "name",
                    "properties": {
                        "name": "EPSG:" + code
                    }
                };
            }
        }
        return crs;
    },
    extract: {
        'feature': function (feature) {
            var geom = this.extract.geometry.apply(this, [feature.geometry]);
            var json = {
                "type": "Feature",
                "properties": feature.attributes,
                "geometry": geom
            };
            if (feature.fid != null) {
                json.id = feature.fid;
            }
            return json;
        },
        'geometry': function (geometry) {
            if (geometry == null) {
                return null;
            }
            if (this.internalProjection && this.externalProjection) {
                geometry = geometry.clone();
                geometry.transform(this.internalProjection, this.externalProjection);
            }
            var geometryType = geometry.CLASS_NAME.split('.')[2];
            var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]);
            var json;
            if (geometryType == "Collection") {
                json = {
                    "type": "GeometryCollection",
                    "geometries": data
                };
            } else {
                json = {
                    "type": geometryType,
                    "coordinates": data
                };
            }
            return json;
        },
        'point': function (point) {
            return [point.x, point.y];
        },
        'multipoint': function (multipoint) {
            var array = [];
            for (var i = 0, len = multipoint.components.length; i < len; ++i) {
                array.push(this.extract.point.apply(this, [multipoint.components[i]]));
            }
            return array;
        },
        'linestring': function (linestring) {
            var array = [];
            for (var i = 0, len = linestring.components.length; i < len; ++i) {
                array.push(this.extract.point.apply(this, [linestring.components[i]]));
            }
            return array;
        },
        'multilinestring': function (multilinestring) {
            var array = [];
            for (var i = 0, len = multilinestring.components.length; i < len; ++i) {
                array.push(this.extract.linestring.apply(this, [multilinestring.components[i]]));
            }
            return array;
        },
        'polygon': function (polygon) {
            var array = [];
            for (var i = 0, len = polygon.components.length; i < len; ++i) {
                array.push(this.extract.linestring.apply(this, [polygon.components[i]]));
            }
            return array;
        },
        'multipolygon': function (multipolygon) {
            var array = [];
            for (var i = 0, len = multipolygon.components.length; i < len; ++i) {
                array.push(this.extract.polygon.apply(this, [multipolygon.components[i]]));
            }
            return array;
        },
        'collection': function (collection) {
            var len = collection.components.length;
            var array = new Array(len);
            for (var i = 0; i < len; ++i) {
                array[i] = this.extract.geometry.apply(this, [collection.components[i]]);
            }
            return array;
        }
    },
    CLASS_NAME: "OpenLayers.Format.GeoJSON"
});
OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {
    url: null,
    params: null,
    callback: null,
    scope: null,
    format: null,
    callbackKey: "callback",
    callbackPrefix: "",
    pendingRequests: null,
    srsInBBOX: false,
    initialize: function (options) {
        options = options || {};
        this.params = {};
        this.pendingRequests = {};
        OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
        if (!this.format) {
            this.format = new OpenLayers.Format.GeoJSON();
        }
        if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
            var format = new OpenLayers.Format.QueryStringFilter({
                srsInBBOX: this.srsInBBOX
            });
            this.filterToParams = function (filter, params) {
                return format.write(filter, params);
            }
        }
    },
    read: function (options) {
        OpenLayers.Protocol.prototype.read.apply(this, arguments);
        options = OpenLayers.Util.applyDefaults(options, this.options);
        options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);
        if (options.filter && this.filterToParams) {
            options.params = this.filterToParams(options.filter, options.params);
        }
        var response = new OpenLayers.Protocol.Response({
            requestType: "read"
        });
        var request = this.createRequest(options.url, options.params, OpenLayers.Function.bind(function (data) {
            response.data = data;
            this.handleRead(response, options);
        }, this));
        response.priv = request;
        return response;
    },
    createRequest: function (url, params, callback) {
        var id = OpenLayers.Protocol.Script.register(callback);
        var name = "OpenLayers.Protocol.Script.registry[" + id + "]";
        params = OpenLayers.Util.extend({}, params);
        params[this.callbackKey] = this.callbackPrefix + name;
        url = OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(params));
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        script.id = "OpenLayers_Protocol_Script_" + id;
        this.pendingRequests[script.id] = script;
        var head = document.getElementsByTagName("head")[0];
        head.appendChild(script);
        return script;
    },
    destroyRequest: function (script) {
        OpenLayers.Protocol.Script.unregister(script.id.split("_").pop());
        delete this.pendingRequests[script.id];
        if (script.parentNode) {
            script.parentNode.removeChild(script);
        }
    },
    handleRead: function (response, options) {
        this.handleResponse(response, options);
    },
    handleResponse: function (response, options) {
        if (options.callback) {
            if (response.data) {
                response.features = this.parseFeatures(response.data);
                response.code = OpenLayers.Protocol.Response.SUCCESS;
            } else {
                response.code = OpenLayers.Protocol.Response.FAILURE;
            }
            this.destroyRequest(response.priv);
            options.callback.call(options.scope, response);
        }
    },
    parseFeatures: function (data) {
        return this.format.read(data);
    },
    abort: function (response) {
        if (response) {
            this.destroyRequest(response.priv);
        } else {
            for (var key in this.pendingRequests) {
                this.destroyRequest(this.pendingRequests[key]);
            }
        }
    },
    destroy: function () {
        this.abort();
        delete this.params;
        delete this.format;
        OpenLayers.Protocol.prototype.destroy.apply(this);
    },
    CLASS_NAME: "OpenLayers.Protocol.Script"
});
(function () {
    var o = OpenLayers.Protocol.Script;
    var counter = 0;
    o.registry = [];
    o.register = function (callback) {
        var id = ++counter;
        o.registry[id] = function () {
            o.unregister(id);
            callback.apply(this, arguments);
        };
        return id;
    };
    o.unregister = function (id) {
        delete o.registry[id];
    };
})();
OpenLayers.Format.CSWGetRecords = function (options) {
    options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetRecords.DEFAULTS);
    var cls = OpenLayers.Format.CSWGetRecords["v" + options.version.replace(/\./g, "_")];
    if (!cls) {
        throw "Unsupported CSWGetRecords version: " + options.version;
    }
    return new cls(options);
};
OpenLayers.Format.CSWGetRecords.DEFAULTS = {
    "version": "2.0.2"
};
OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, {
    controls: null,
    autoActivate: true,
    defaultControl: null,
    saveState: false,
    allowDepress: false,
    activeState: null,
    initialize: function (options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.controls = [];
        this.activeState = {};
    },
    destroy: function () {
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
        for (var ctl, i = this.controls.length - 1; i >= 0; i--) {
            ctl = this.controls[i];
            if (ctl.events) {
                ctl.events.un({
                    activate: this.iconOn,
                    deactivate: this.iconOff
                });
            }
            OpenLayers.Event.stopObservingElement(ctl.panel_div);
            ctl.panel_div = null;
        }
        this.activeState = null;
    },
    activate: function () {
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            var control;
            for (var i = 0, len = this.controls.length; i < len; i++) {
                control = this.controls[i];
                if (control === this.defaultControl || (this.saveState && this.activeState[control.id])) {
                    control.activate();
                }
            }
            if (this.saveState === true) {
                this.defaultControl = null;
            }
            this.redraw();
            return true;
        } else {
            return false;
        }
    },
    deactivate: function () {
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            var control;
            for (var i = 0, len = this.controls.length; i < len; i++) {
                control = this.controls[i];
                this.activeState[control.id] = control.deactivate();
            }
            this.redraw();
            return true;
        } else {
            return false;
        }
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        this.addControlsToMap(this.controls);
        return this.div;
    },
    redraw: function () {
        for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) {
            this.div.removeChild(this.div.childNodes[i]);
        }
        this.div.innerHTML = "";
        if (this.active) {
            for (var i = 0, len = this.controls.length; i < len; i++) {
                this.div.appendChild(this.controls[i].panel_div);
            }
        }
    },
    activateControl: function (control) {
        if (!this.active) {
            return false;
        }
        if (control.type == OpenLayers.Control.TYPE_BUTTON) {
            control.trigger();
            return;
        }
        if (control.type == OpenLayers.Control.TYPE_TOGGLE) {
            if (control.active) {
                control.deactivate();
            } else {
                control.activate();
            }
            return;
        }
        if (this.allowDepress && control.active) {
            control.deactivate();
        } else {
            var c;
            for (var i = 0, len = this.controls.length; i < len; i++) {
                c = this.controls[i];
                if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) {
                    c.deactivate();
                }
            }
            control.activate();
        }
    },
    addControls: function (controls) {
        if (!(OpenLayers.Util.isArray(controls))) {
            controls = [controls];
        }
        this.controls = this.controls.concat(controls);
        for (var i = 0, len = controls.length; i < len; i++) {
            var element = document.createElement("div");
            element.className = controls[i].displayClass + "ItemInactive";
            controls[i].panel_div = element;
            if (controls[i].title != "") {
                controls[i].panel_div.title = controls[i].title;
            }
            OpenLayers.Event.observe(controls[i].panel_div, "click", OpenLayers.Function.bind(this.onClick, this, controls[i]));
            OpenLayers.Event.observe(controls[i].panel_div, "dblclick", OpenLayers.Function.bind(this.onDoubleClick, this, controls[i]));
            OpenLayers.Event.observe(controls[i].panel_div, "mousedown", OpenLayers.Function.bindAsEventListener(OpenLayers.Event.stop));
        }
        if (this.map) {
            this.addControlsToMap(controls);
            this.redraw();
        }
    },
    addControlsToMap: function (controls) {
        var control;
        for (var i = 0, len = controls.length; i < len; i++) {
            control = controls[i];
            if (control.autoActivate === true) {
                control.autoActivate = false;
                this.map.addControl(control);
                control.autoActivate = true;
            } else {
                this.map.addControl(control);
                control.deactivate();
            }
            control.events.on({
                activate: this.iconOn,
                deactivate: this.iconOff
            });
        }
    },
    iconOn: function () {
        var d = this.panel_div;
        d.className = d.className.replace(/ItemInactive$/, "ItemActive");
    },
    iconOff: function () {
        var d = this.panel_div;
        d.className = d.className.replace(/ItemActive$/, "ItemInactive");
    },
    onClick: function (ctrl, evt) {
        OpenLayers.Event.stop(evt ? evt : window.event);
        this.activateControl(ctrl);
    },
    onDoubleClick: function (ctrl, evt) {
        OpenLayers.Event.stop(evt ? evt : window.event);
    },
    getControlsBy: function (property, match) {
        var test = (typeof match.test == "function");
        var found = OpenLayers.Array.filter(this.controls, function (item) {
            return item[property] == match || (test && match.test(item[property]));
        });
        return found;
    },
    getControlsByName: function (match) {
        return this.getControlsBy("name", match);
    },
    getControlsByClass: function (match) {
        return this.getControlsBy("CLASS_NAME", match);
    },
    CLASS_NAME: "OpenLayers.Control.Panel"
});
OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_BUTTON,
    trigger: function () {
        this.map.zoomIn();
    },
    CLASS_NAME: "OpenLayers.Control.ZoomIn"
});
OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_BUTTON,
    trigger: function () {
        this.map.zoomOut();
    },
    CLASS_NAME: "OpenLayers.Control.ZoomOut"
});
OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_BUTTON,
    trigger: function () {
        if (this.map) {
            this.map.zoomToMaxExtent();
        }
    },
    CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
});
OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
    initialize: function (options) {
        OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
        this.addControls([new OpenLayers.Control.ZoomIn(), new OpenLayers.Control.ZoomToMaxExtent(), new OpenLayers.Control.ZoomOut()]);
    },
    CLASS_NAME: "OpenLayers.Control.ZoomPanel"
});
OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, {
    URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2,
    url: null,
    params: null,
    reproject: false,
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
        this.url = url;
        this.params = OpenLayers.Util.extend({}, params);
    },
    destroy: function () {
        this.url = null;
        this.params = null;
        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
        return obj;
    },
    setUrl: function (newUrl) {
        this.url = newUrl;
    },
    mergeNewParams: function (newParams) {
        this.params = OpenLayers.Util.extend(this.params, newParams);
        var ret = this.redraw();
        if (this.map != null) {
            this.map.events.triggerEvent("changelayer", {
                layer: this,
                property: "params"
            });
        }
        return ret;
    },
    redraw: function (force) {
        if (force) {
            return this.mergeNewParams({
                "_olSalt": Math.random()
            });
        } else {
            return OpenLayers.Layer.prototype.redraw.apply(this, []);
        }
    },
    selectUrl: function (paramString, urls) {
        var product = 1;
        for (var i = 0, len = paramString.length; i < len; i++) {
            product *= paramString.charCodeAt(i) * this.URL_HASH_FACTOR;
            product -= Math.floor(product);
        }
        return urls[Math.floor(product * urls.length)];
    },
    getFullRequestString: function (newParams, altUrl) {
        var url = altUrl || this.url;
        var allParams = OpenLayers.Util.extend({}, this.params);
        allParams = OpenLayers.Util.extend(allParams, newParams);
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(paramsString, url);
        }
        var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
        for (var key in allParams) {
            if (key.toUpperCase() in urlParams) {
                delete allParams[key];
            }
        }
        paramsString = OpenLayers.Util.getParameterString(allParams);
        return OpenLayers.Util.urlAppend(url, paramsString);
    },
    CLASS_NAME: "OpenLayers.Layer.HTTPRequest"
});
OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
    tileSize: null,
    tileOriginCorner: "bl",
    tileOrigin: null,
    tileOptions: null,
    grid: null,
    singleTile: false,
    ratio: 1.5,
    buffer: 0,
    numLoadingTiles: 0,
    tileLoadingDelay: 100,
    timerId: null,
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments);
        this.events.addEventType("tileloaded");
        this.grid = [];
        this._moveGriddedTiles = OpenLayers.Function.bind(this.moveGriddedTiles, this);
    },
    removeMap: function (map) {
        if (this.timerId != null) {
            window.clearTimeout(this.timerId);
            this.timerId = null;
        }
    },
    destroy: function () {
        this.clearGrid();
        this.grid = null;
        this.tileSize = null;
        OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
    },
    clearGrid: function () {
        if (this.grid) {
            for (var iRow = 0, len = this.grid.length; iRow < len; iRow++) {
                var row = this.grid[iRow];
                for (var iCol = 0, clen = row.length; iCol < clen; iCol++) {
                    var tile = row[iCol];
                    this.removeTileMonitoringHooks(tile);
                    tile.destroy();
                }
            }
            this.grid = [];
        }
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]);
        if (this.tileSize != null) {
            obj.tileSize = this.tileSize.clone();
        }
        obj.grid = [];
        return obj;
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
        bounds = bounds || this.map.getExtent();
        if (bounds != null) {
            var forceReTile = !this.grid.length || zoomChanged;
            var tilesBounds = this.getTilesBounds();
            if (this.singleTile) {
                if (forceReTile || (!dragging && !tilesBounds.containsBounds(bounds))) {
                    this.initSingleTile(bounds);
                }
            } else {
                if (forceReTile || !tilesBounds.containsBounds(bounds, true)) {
                    this.initGriddedTiles(bounds);
                } else {
                    this.scheduleMoveGriddedTiles();
                }
            }
        }
    },
    moveByPx: function (dx, dy) {
        if (!this.singleTile) {
            this.scheduleMoveGriddedTiles();
        }
    },
    scheduleMoveGriddedTiles: function () {
        if (this.timerId != null) {
            window.clearTimeout(this.timerId);
        }
        this.timerId = window.setTimeout(this._moveGriddedTiles, this.tileLoadingDelay);
    },
    setTileSize: function (size) {
        if (this.singleTile) {
            size = this.map.getSize();
            size.h = parseInt(size.h * this.ratio);
            size.w = parseInt(size.w * this.ratio);
        }
        OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]);
    },
    getGridBounds: function () {
        var msg = "The getGridBounds() function is deprecated. It will be " + "removed in 3.0. Please use getTilesBounds() instead.";
        OpenLayers.Console.warn(msg);
        return this.getTilesBounds();
    },
    getTilesBounds: function () {
        var bounds = null;
        if (this.grid.length) {
            var bottom = this.grid.length - 1;
            var bottomLeftTile = this.grid[bottom][0];
            var right = this.grid[0].length - 1;
            var topRightTile = this.grid[0][right];
            bounds = new OpenLayers.Bounds(bottomLeftTile.bounds.left, bottomLeftTile.bounds.bottom, topRightTile.bounds.right, topRightTile.bounds.top);
        }
        return bounds;
    },
    initSingleTile: function (bounds) {
        var center = bounds.getCenterLonLat();
        var tileWidth = bounds.getWidth() * this.ratio;
        var tileHeight = bounds.getHeight() * this.ratio;
        var tileBounds = new OpenLayers.Bounds(center.lon - (tileWidth / 2), center.lat - (tileHeight / 2), center.lon + (tileWidth / 2), center.lat + (tileHeight / 2));
        var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
        var px = this.map.getLayerPxFromLonLat(ul);
        if (!this.grid.length) {
            this.grid[0] = [];
        }
        var tile = this.grid[0][0];
        if (!tile) {
            tile = this.addTile(tileBounds, px);
            this.addTileMonitoringHooks(tile);
            tile.draw();
            this.grid[0][0] = tile;
        } else {
            tile.moveTo(tileBounds, px);
        }
        this.removeExcessTiles(1, 1);
    },
    calculateGridLayout: function (bounds, origin, resolution) {
        var tilelon = resolution * this.tileSize.w;
        var tilelat = resolution * this.tileSize.h;
        var offsetlon = bounds.left - origin.lon;
        var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;
        var tilecolremain = offsetlon / tilelon - tilecol;
        var tileoffsetx = -tilecolremain * this.tileSize.w;
        var tileoffsetlon = origin.lon + tilecol * tilelon;
        var offsetlat = bounds.top - (origin.lat + tilelat);
        var tilerow = Math.ceil(offsetlat / tilelat) + this.buffer;
        var tilerowremain = tilerow - offsetlat / tilelat;
        var tileoffsety = -tilerowremain * this.tileSize.h;
        var tileoffsetlat = origin.lat + tilerow * tilelat;
        return {
            tilelon: tilelon,
            tilelat: tilelat,
            tileoffsetlon: tileoffsetlon,
            tileoffsetlat: tileoffsetlat,
            tileoffsetx: tileoffsetx,
            tileoffsety: tileoffsety
        };
    },
    getTileOrigin: function () {
        var origin = this.tileOrigin;
        if (!origin) {
            var extent = this.getMaxExtent();
            var edges = ({
                "tl": ["left", "top"],
                "tr": ["right", "top"],
                "bl": ["left", "bottom"],
                "br": ["right", "bottom"]
            })[this.tileOriginCorner];
            origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]);
        }
        return origin;
    },
    initGriddedTiles: function (bounds) {
        var viewSize = this.map.getSize();
        var minRows = Math.ceil(viewSize.h / this.tileSize.h) + Math.max(1, 2 * this.buffer);
        var minCols = Math.ceil(viewSize.w / this.tileSize.w) + Math.max(1, 2 * this.buffer);
        var origin = this.getTileOrigin();
        var resolution = this.map.getResolution();
        var tileLayout = this.calculateGridLayout(bounds, origin, resolution);
        var tileoffsetx = Math.round(tileLayout.tileoffsetx);
        var tileoffsety = Math.round(tileLayout.tileoffsety);
        var tileoffsetlon = tileLayout.tileoffsetlon;
        var tileoffsetlat = tileLayout.tileoffsetlat;
        var tilelon = tileLayout.tilelon;
        var tilelat = tileLayout.tilelat;
        this.origin = new OpenLayers.Pixel(tileoffsetx, tileoffsety);
        var startX = tileoffsetx;
        var startLon = tileoffsetlon;
        var rowidx = 0;
        var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
        var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
        do {
            var row = this.grid[rowidx++];
            if (!row) {
                row = [];
                this.grid.push(row);
            }
            tileoffsetlon = startLon;
            tileoffsetx = startX;
            var colidx = 0;
            do {
                var tileBounds = new OpenLayers.Bounds(tileoffsetlon, tileoffsetlat, tileoffsetlon + tilelon, tileoffsetlat + tilelat);
                var x = tileoffsetx;
                x -= layerContainerDivLeft;
                var y = tileoffsety;
                y -= layerContainerDivTop;
                var px = new OpenLayers.Pixel(x, y);
                var tile = row[colidx++];
                if (!tile) {
                    tile = this.addTile(tileBounds, px);
                    this.addTileMonitoringHooks(tile);
                    row.push(tile);
                } else {
                    tile.moveTo(tileBounds, px, false);
                }
                tileoffsetlon += tilelon;
                tileoffsetx += this.tileSize.w;
            } while ((tileoffsetlon <= bounds.right + tilelon * this.buffer) || colidx < minCols);
            tileoffsetlat -= tilelat;
            tileoffsety += this.tileSize.h;
        } while ((tileoffsetlat >= bounds.bottom - tilelat * this.buffer) || rowidx < minRows);
        this.removeExcessTiles(rowidx, colidx);
        this.spiralTileLoad();
    },
    getMaxExtent: function () {
        return this.maxExtent;
    },
    spiralTileLoad: function () {
        var tileQueue = [];
        var directions = ["right", "down", "left", "up"];
        var iRow = 0;
        var iCell = -1;
        var direction = OpenLayers.Util.indexOf(directions, "right");
        var directionsTried = 0;
        while (directionsTried < directions.length) {
            var testRow = iRow;
            var testCell = iCell;
            switch (directions[direction]) {
            case "right":
                testCell++;
                break;
            case "down":
                testRow++;
                break;
            case "left":
                testCell--;
                break;
            case "up":
                testRow--;
                break;
            }
            var tile = null;
            if ((testRow < this.grid.length) && (testRow >= 0) && (testCell < this.grid[0].length) && (testCell >= 0)) {
                tile = this.grid[testRow][testCell];
            }
            if ((tile != null) && (!tile.queued)) {
                tileQueue.unshift(tile);
                tile.queued = true;
                directionsTried = 0;
                iRow = testRow;
                iCell = testCell;
            } else {
                direction = (direction + 1) % 4;
                directionsTried++;
            }
        }
        for (var i = 0, len = tileQueue.length; i < len; i++) {
            var tile = tileQueue[i];
            tile.draw();
            tile.queued = false;
        }
    },
    addTile: function (bounds, position) {
        return new OpenLayers.Tile.Image(this, position, bounds, null, this.tileSize, this.tileOptions);
    },
    addTileMonitoringHooks: function (tile) {
        tile.onLoadStart = function () {
            if (this.numLoadingTiles == 0) {
                this.events.triggerEvent("loadstart");
            }
            this.numLoadingTiles++;
        };
        tile.events.register("loadstart", this, tile.onLoadStart);
        tile.onLoadEnd = function () {
            this.numLoadingTiles--;
            this.events.triggerEvent("tileloaded");
            if (this.numLoadingTiles == 0) {
                this.events.triggerEvent("loadend");
            }
        };
        tile.events.register("loadend", this, tile.onLoadEnd);
        tile.events.register("unload", this, tile.onLoadEnd);
    },
    removeTileMonitoringHooks: function (tile) {
        tile.unload();
        tile.events.un({
            "loadstart": tile.onLoadStart,
            "loadend": tile.onLoadEnd,
            "unload": tile.onLoadEnd,
            scope: this
        });
    },
    moveGriddedTiles: function () {
        var shifted = true;
        var buffer = this.buffer || 1;
        var tlLayer = this.grid[0][0].position;
        var offsetX = parseInt(this.map.layerContainerDiv.style.left);
        var offsetY = parseInt(this.map.layerContainerDiv.style.top);
        var tlViewPort = tlLayer.add(offsetX, offsetY);
        if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
            this.shiftColumn(true);
        } else if (tlViewPort.x < -this.tileSize.w * buffer) {
            this.shiftColumn(false);
        } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) {
            this.shiftRow(true);
        } else if (tlViewPort.y < -this.tileSize.h * buffer) {
            this.shiftRow(false);
        } else {
            shifted = false;
        }
        if (shifted) {
            this.timerId = window.setTimeout(this._moveGriddedTiles, 0);
        }
    },
    shiftRow: function (prepend) {
        var modelRowIndex = (prepend) ? 0 : (this.grid.length - 1);
        var grid = this.grid;
        var modelRow = grid[modelRowIndex];
        var resolution = this.map.getResolution();
        var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
        var deltaLat = resolution * -deltaY;
        var row = (prepend) ? grid.pop() : grid.shift();
        for (var i = 0, len = modelRow.length; i < len; i++) {
            var modelTile = modelRow[i];
            var bounds = modelTile.bounds.clone();
            var position = modelTile.position.clone();
            bounds.bottom = bounds.bottom + deltaLat;
            bounds.top = bounds.top + deltaLat;
            position.y = position.y + deltaY;
            row[i].moveTo(bounds, position);
        }
        if (prepend) {
            grid.unshift(row);
        } else {
            grid.push(row);
        }
    },
    shiftColumn: function (prepend) {
        var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
        var resolution = this.map.getResolution();
        var deltaLon = resolution * deltaX;
        for (var i = 0, len = this.grid.length; i < len; i++) {
            var row = this.grid[i];
            var modelTileIndex = (prepend) ? 0 : (row.length - 1);
            var modelTile = row[modelTileIndex];
            var bounds = modelTile.bounds.clone();
            var position = modelTile.position.clone();
            bounds.left = bounds.left + deltaLon;
            bounds.right = bounds.right + deltaLon;
            position.x = position.x + deltaX;
            var tile = prepend ? this.grid[i].pop() : this.grid[i].shift();
            tile.moveTo(bounds, position);
            if (prepend) {
                row.unshift(tile);
            } else {
                row.push(tile);
            }
        }
    },
    removeExcessTiles: function (rows, columns) {
        while (this.grid.length > rows) {
            var row = this.grid.pop();
            for (var i = 0, l = row.length; i < l; i++) {
                var tile = row[i];
                this.removeTileMonitoringHooks(tile);
                tile.destroy();
            }
        }
        while (this.grid[0].length > columns) {
            for (var i = 0, l = this.grid.length; i < l; i++) {
                var row = this.grid[i];
                var tile = row.pop();
                this.removeTileMonitoringHooks(tile);
                tile.destroy();
            }
        }
    },
    onMapResize: function () {
        if (this.singleTile) {
            this.clearGrid();
            this.setTileSize();
        }
    },
    getTileBounds: function (viewPortPx) {
        var maxExtent = this.maxExtent;
        var resolution = this.getResolution();
        var tileMapWidth = resolution * this.tileSize.w;
        var tileMapHeight = resolution * this.tileSize.h;
        var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
        var tileLeft = maxExtent.left + (tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth));
        var tileBottom = maxExtent.bottom + (tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight));
        return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight);
    },
    CLASS_NAME: "OpenLayers.Layer.Grid"
});
OpenLayers.Tile = OpenLayers.Class({
    EVENT_TYPES: ["loadstart", "loadend", "reload", "unload"],
    events: null,
    id: null,
    layer: null,
    url: null,
    bounds: null,
    size: null,
    position: null,
    isLoading: false,
    initialize: function (layer, position, bounds, url, size, options) {
        this.layer = layer;
        this.position = position.clone();
        this.bounds = bounds.clone();
        this.url = url;
        if (size) {
            this.size = size.clone();
        }
        this.id = OpenLayers.Util.createUniqueID("Tile_");
        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
        OpenLayers.Util.extend(this, options);
    },
    unload: function () {
        if (this.isLoading) {
            this.isLoading = false;
            this.events.triggerEvent("unload");
        }
    },
    destroy: function () {
        this.layer = null;
        this.bounds = null;
        this.size = null;
        this.position = null;
        this.events.destroy();
        this.events = null;
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Tile(this.layer, this.position, this.bounds, this.url, this.size);
        }
        OpenLayers.Util.applyDefaults(obj, this);
        return obj;
    },
    draw: function () {
        var maxExtent = this.layer.maxExtent;
        var withinMaxExtent = (maxExtent && this.bounds.intersectsBounds(maxExtent, false));
        this.shouldDraw = (withinMaxExtent || this.layer.displayOutsideMaxExtent);
        this.clear();
        return this.shouldDraw;
    },
    moveTo: function (bounds, position, redraw) {
        if (redraw == null) {
            redraw = true;
        }
        this.bounds = bounds.clone();
        this.position = position.clone();
        if (redraw) {
            this.draw();
        }
    },
    clear: function () {},
    getBoundsFromBaseLayer: function (position) {
        var msg = OpenLayers.i18n('reprojectDeprecated', {
            'layerName': this.layer.name
        });
        OpenLayers.Console.warn(msg);
        var topLeft = this.layer.map.getLonLatFromLayerPx(position);
        var bottomRightPx = position.clone();
        bottomRightPx.x += this.size.w;
        bottomRightPx.y += this.size.h;
        var bottomRight = this.layer.map.getLonLatFromLayerPx(bottomRightPx);
        if (topLeft.lon > bottomRight.lon) {
            if (topLeft.lon < 0) {
                topLeft.lon = -180 - (topLeft.lon + 180);
            } else {
                bottomRight.lon = 180 + bottomRight.lon + 180;
            }
        }
        var bounds = new OpenLayers.Bounds(topLeft.lon, bottomRight.lat, bottomRight.lon, topLeft.lat);
        return bounds;
    },
    showTile: function () {
        if (this.shouldDraw) {
            this.show();
        }
    },
    show: function () {},
    hide: function () {},
    CLASS_NAME: "OpenLayers.Tile"
});
OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
    url: null,
    imgDiv: null,
    frame: null,
    layerAlphaHack: null,
    isBackBuffer: false,
    isFirstDraw: true,
    backBufferTile: null,
    maxGetUrlLength: null,
    initialize: function (layer, position, bounds, url, size, options) {
        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
        if (this.maxGetUrlLength != null) {
            OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame);
        }
        this.url = url;
        this.frame = document.createElement('div');
        this.frame.style.overflow = 'hidden';
        this.frame.style.position = 'absolute';
        this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack();
    },
    destroy: function () {
        if (this.imgDiv != null) {
            this.removeImgDiv();
        }
        this.imgDiv = null;
        if ((this.frame != null) && (this.frame.parentNode == this.layer.div)) {
            this.layer.div.removeChild(this.frame);
        }
        this.frame = null;
        if (this.backBufferTile) {
            this.backBufferTile.destroy();
            this.backBufferTile = null;
        }
        this.layer.events.unregister("loadend", this, this.resetBackBuffer);
        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Tile.Image(this.layer, this.position, this.bounds, this.url, this.size);
        }
        obj = OpenLayers.Tile.prototype.clone.apply(this, [obj]);
        obj.imgDiv = null;
        return obj;
    },
    draw: function () {
        if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
            this.bounds = this.getBoundsFromBaseLayer(this.position);
        }
        var drawTile = OpenLayers.Tile.prototype.draw.apply(this, arguments);
        if ((OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) || this.layer.singleTile) {
            if (drawTile) {
                if (!this.backBufferTile) {
                    this.backBufferTile = this.clone();
                    this.backBufferTile.hide();
                    this.backBufferTile.isBackBuffer = true;
                    this.events.register('loadend', this, this.resetBackBuffer);
                    this.layer.events.register("loadend", this, this.resetBackBuffer);
                }
                this.startTransition();
            } else {
                if (this.backBufferTile) {
                    this.backBufferTile.clear();
                }
            }
        } else {
            if (drawTile && this.isFirstDraw) {
                this.events.register('loadend', this, this.showTile);
                this.isFirstDraw = false;
            }
        }
        if (!drawTile) {
            return false;
        }
        if (this.isLoading) {
            this.events.triggerEvent("reload");
        } else {
            this.isLoading = true;
            this.events.triggerEvent("loadstart");
        }
        return this.renderTile();
    },
    resetBackBuffer: function () {
        this.showTile();
        if (this.backBufferTile && (this.isFirstDraw || !this.layer.numLoadingTiles)) {
            this.isFirstDraw = false;
            var maxExtent = this.layer.maxExtent;
            var withinMaxExtent = (maxExtent && this.bounds.intersectsBounds(maxExtent, false));
            if (withinMaxExtent) {
                this.backBufferTile.position = this.position;
                this.backBufferTile.bounds = this.bounds;
                this.backBufferTile.size = this.size;
                this.backBufferTile.imageSize = this.layer.getImageSize(this.bounds) || this.size;
                this.backBufferTile.imageOffset = this.layer.imageOffset;
                this.backBufferTile.resolution = this.layer.getResolution();
                this.backBufferTile.renderTile();
            }
            this.backBufferTile.hide();
        }
    },
    renderTile: function () {
        if (this.layer.async) {
            this.initImgDiv();
            this.layer.getURLasync(this.bounds, this, "url", this.positionImage);
        } else {
            this.url = this.layer.getURL(this.bounds);
            this.initImgDiv();
            this.positionImage();
        }
        return true;
    },
    positionImage: function () {
        if (this.layer === null) {
            return;
        }
        OpenLayers.Util.modifyDOMElement(this.frame, null, this.position, this.size);
        var imageSize = this.layer.getImageSize(this.bounds);
        if (this.layerAlphaHack) {
            OpenLayers.Util.modifyAlphaImageDiv(this.imgDiv, null, null, imageSize, this.url);
        } else {
            OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, imageSize);
            this.imgDiv.src = this.url;
        }
    },
    clear: function () {
        if (this.imgDiv) {
            this.hide();
            if (OpenLayers.Tile.Image.useBlankTile) {
                this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
            }
        }
    },
    initImgDiv: function () {
        if (this.imgDiv == null) {
            var offset = this.layer.imageOffset;
            var size = this.layer.getImageSize(this.bounds);
            if (this.layerAlphaHack) {
                this.imgDiv = OpenLayers.Util.createAlphaImageDiv(null, offset, size, null, "relative", null, null, null, true);
            } else {
                this.imgDiv = OpenLayers.Util.createImage(null, offset, size, null, "relative", null, null, true);
            }
            if (OpenLayers.Util.isArray(this.layer.url)) {
                this.imgDiv.urls = this.layer.url.slice();
            }
            this.imgDiv.className = 'olTileImage';
            this.frame.style.zIndex = this.isBackBuffer ? 0 : 1;
            this.frame.appendChild(this.imgDiv);
            this.layer.div.appendChild(this.frame);
            if (this.layer.opacity != null) {
                OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null, null, null, null, this.layer.opacity);
            }
            this.imgDiv.map = this.layer.map;
            var onload = function () {
                    if (this.isLoading) {
                        this.isLoading = false;
                        this.events.triggerEvent("loadend");
                    }
                };
            if (this.layerAlphaHack) {
                OpenLayers.Event.observe(this.imgDiv.childNodes[0], 'load', OpenLayers.Function.bind(onload, this));
            } else {
                OpenLayers.Event.observe(this.imgDiv, 'load', OpenLayers.Function.bind(onload, this));
            }
            var onerror = function () {
                    if (this.imgDiv._attempts > OpenLayers.IMAGE_RELOAD_ATTEMPTS) {
                        onload.call(this);
                    }
                };
            OpenLayers.Event.observe(this.imgDiv, "error", OpenLayers.Function.bind(onerror, this));
        }
        this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
    },
    removeImgDiv: function () {
        OpenLayers.Event.stopObservingElement(this.imgDiv);
        if (this.imgDiv.parentNode == this.frame) {
            this.frame.removeChild(this.imgDiv);
            this.imgDiv.map = null;
        }
        this.imgDiv.urls = null;
        var child = this.imgDiv.firstChild;
        if (child) {
            OpenLayers.Event.stopObservingElement(child);
            this.imgDiv.removeChild(child);
            delete child;
        } else {
            this.imgDiv.src = OpenLayers.Util.getImagesLocation() + "blank.gif";
        }
    },
    checkImgURL: function () {
        if (this.layer) {
            var loaded = this.layerAlphaHack ? this.imgDiv.firstChild.src : this.imgDiv.src;
            if (!OpenLayers.Util.isEquivalentUrl(loaded, this.url)) {
                this.hide();
            }
        }
    },
    startTransition: function () {
        if (!this.backBufferTile || !this.backBufferTile.imgDiv) {
            return;
        }
        var ratio = 1;
        if (this.backBufferTile.resolution) {
            ratio = this.backBufferTile.resolution / this.layer.getResolution();
        }
        if (ratio != 1) {
            if (this.layer.transitionEffect == 'resize') {
                var upperLeft = new OpenLayers.LonLat(this.backBufferTile.bounds.left, this.backBufferTile.bounds.top);
                var size = new OpenLayers.Size(this.backBufferTile.size.w * ratio, this.backBufferTile.size.h * ratio);
                var px = this.layer.map.getLayerPxFromLonLat(upperLeft);
                OpenLayers.Util.modifyDOMElement(this.backBufferTile.frame, null, px, size);
                var imageSize = this.backBufferTile.imageSize;
                imageSize = new OpenLayers.Size(imageSize.w * ratio, imageSize.h * ratio);
                var imageOffset = this.backBufferTile.imageOffset;
                if (imageOffset) {
                    imageOffset = new OpenLayers.Pixel(imageOffset.x * ratio, imageOffset.y * ratio);
                }
                OpenLayers.Util.modifyDOMElement(this.backBufferTile.imgDiv, null, imageOffset, imageSize);
                this.backBufferTile.show();
            }
        } else {
            if (this.layer.singleTile) {
                this.backBufferTile.show();
            } else {
                this.backBufferTile.hide();
            }
        }
    },
    show: function () {
        this.frame.style.display = '';
        if (OpenLayers.Util.indexOf(this.layer.SUPPORTED_TRANSITIONS, this.layer.transitionEffect) != -1) {
            if (OpenLayers.IS_GECKO === true) {
                this.frame.scrollLeft = this.frame.scrollLeft;
            }
        }
    },
    hide: function () {
        this.frame.style.display = 'none';
    },
    CLASS_NAME: "OpenLayers.Tile.Image"
});
OpenLayers.Tile.Image.useBlankTile = (OpenLayers.BROWSER_NAME == "safari" || OpenLayers.BROWSER_NAME == "opera");
OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, {
    fontStyleKeys: ['antialiasing', 'blockout', 'font', 'fontcolor', 'fontsize', 'fontstyle', 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency'],
    request: null,
    response: null,
    initialize: function (options) {
        this.request = new OpenLayers.Format.ArcXML.Request();
        this.response = new OpenLayers.Format.ArcXML.Response();
        if (options) {
            if (options.requesttype == "feature") {
                this.request.get_image = null;
                var qry = this.request.get_feature.query;
                this.addCoordSys(qry.featurecoordsys, options.featureCoordSys);
                this.addCoordSys(qry.filtercoordsys, options.filterCoordSys);
                if (options.polygon) {
                    qry.isspatial = true;
                    qry.spatialfilter.polygon = options.polygon;
                } else if (options.envelope) {
                    qry.isspatial = true;
                    qry.spatialfilter.envelope = {
                        minx: 0,
                        miny: 0,
                        maxx: 0,
                        maxy: 0
                    };
                    this.parseEnvelope(qry.spatialfilter.envelope, options.envelope);
                }
            } else if (options.requesttype == "image") {
                this.request.get_feature = null;
                var props = this.request.get_image.properties;
                this.parseEnvelope(props.envelope, options.envelope);
                this.addLayers(props.layerlist, options.layers);
                this.addImageSize(props.imagesize, options.tileSize);
                this.addCoordSys(props.featurecoordsys, options.featureCoordSys);
                this.addCoordSys(props.filtercoordsys, options.filterCoordSys);
            } else {
                this.request = null;
            }
        }
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    parseEnvelope: function (env, arr) {
        if (arr && arr.length == 4) {
            env.minx = arr[0];
            env.miny = arr[1];
            env.maxx = arr[2];
            env.maxy = arr[3];
        }
    },
    addLayers: function (ll, lyrs) {
        for (var lind = 0, len = lyrs.length; lind < len; lind++) {
            ll.push(lyrs[lind]);
        }
    },
    addImageSize: function (imsize, olsize) {
        if (olsize !== null) {
            imsize.width = olsize.w;
            imsize.height = olsize.h;
            imsize.printwidth = olsize.w;
            imsize.printheight = olsize.h;
        }
    },
    addCoordSys: function (featOrFilt, fsys) {
        if (typeof fsys == "string") {
            featOrFilt.id = parseInt(fsys);
            featOrFilt.string = fsys;
        } else if (typeof fsys == "object" && fsys.proj !== null) {
            featOrFilt.id = fsys.proj.srsProjNumber;
            featOrFilt.string = fsys.proj.srsCode;
        } else {
            featOrFilt = fsys;
        }
    },
    iserror: function (data) {
        var ret = null;
        if (!data) {
            ret = (this.response.error !== '');
        } else {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
            var errorNodes = data.documentElement.getElementsByTagName("ERROR");
            ret = (errorNodes !== null && errorNodes.length > 0);
        }
        return ret;
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var arcNode = null;
        if (data && data.documentElement) {
            if (data.documentElement.nodeName == "ARCXML") {
                arcNode = data.documentElement;
            } else {
                arcNode = data.documentElement.getElementsByTagName("ARCXML")[0];
            }
        }
        if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') {
            var error, source;
            try {
                error = data.firstChild.nodeValue;
                source = data.firstChild.childNodes[1].firstChild.nodeValue;
            } catch (err) {}
            throw {
                message: "Error parsing the ArcXML request",
                error: error,
                source: source
            };
        }
        var response = this.parseResponse(arcNode);
        return response;
    },
    write: function (request) {
        if (!request) {
            request = this.request;
        }
        var root = this.createElementNS("", "ARCXML");
        root.setAttribute("version", "1.1");
        var reqElem = this.createElementNS("", "REQUEST");
        if (request.get_image != null) {
            var getElem = this.createElementNS("", "GET_IMAGE");
            reqElem.appendChild(getElem);
            var propElem = this.createElementNS("", "PROPERTIES");
            getElem.appendChild(propElem);
            var props = request.get_image.properties;
            if (props.featurecoordsys != null) {
                var feat = this.createElementNS("", "FEATURECOORDSYS");
                propElem.appendChild(feat);
                if (props.featurecoordsys.id === 0) {
                    feat.setAttribute("string", props.featurecoordsys['string']);
                } else {
                    feat.setAttribute("id", props.featurecoordsys.id);
                }
            }
            if (props.filtercoordsys != null) {
                var filt = this.createElementNS("", "FILTERCOORDSYS");
                propElem.appendChild(filt);
                if (props.filtercoordsys.id === 0) {
                    filt.setAttribute("string", props.filtercoordsys.string);
                } else {
                    filt.setAttribute("id", props.filtercoordsys.id);
                }
            }
            if (props.envelope != null) {
                var env = this.createElementNS("", "ENVELOPE");
                propElem.appendChild(env);
                env.setAttribute("minx", props.envelope.minx);
                env.setAttribute("miny", props.envelope.miny);
                env.setAttribute("maxx", props.envelope.maxx);
                env.setAttribute("maxy", props.envelope.maxy);
            }
            var imagesz = this.createElementNS("", "IMAGESIZE");
            propElem.appendChild(imagesz);
            imagesz.setAttribute("height", props.imagesize.height);
            imagesz.setAttribute("width", props.imagesize.width);
            if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) {
                imagesz.setAttribute("printheight", props.imagesize.printheight);
                imagesz.setArrtibute("printwidth", props.imagesize.printwidth);
            }
            if (props.background != null) {
                var backgrnd = this.createElementNS("", "BACKGROUND");
                propElem.appendChild(backgrnd);
                backgrnd.setAttribute("color", props.background.color.r + "," + props.background.color.g + "," + props.background.color.b);
                if (props.background.transcolor !== null) {
                    backgrnd.setAttribute("transcolor", props.background.transcolor.r + "," + props.background.transcolor.g + "," + props.background.transcolor.b);
                }
            }
            if (props.layerlist != null && props.layerlist.length > 0) {
                var layerlst = this.createElementNS("", "LAYERLIST");
                propElem.appendChild(layerlst);
                for (var ld = 0; ld < props.layerlist.length; ld++) {
                    var ldef = this.createElementNS("", "LAYERDEF");
                    layerlst.appendChild(ldef);
                    ldef.setAttribute("id", props.layerlist[ld].id);
                    ldef.setAttribute("visible", props.layerlist[ld].visible);
                    if (typeof props.layerlist[ld].query == "object") {
                        var query = props.layerlist[ld].query;
                        if (query.where.length < 0) {
                            continue;
                        }
                        var queryElem = null;
                        if (typeof query.spatialfilter == "boolean" && query.spatialfilter) {
                            queryElem = this.createElementNS("", "SPATIALQUERY");
                        } else {
                            queryElem = this.createElementNS("", "QUERY");
                        }
                        queryElem.setAttribute("where", query.where);
                        if (typeof query.accuracy == "number" && query.accuracy > 0) {
                            queryElem.setAttribute("accuracy", query.accuracy);
                        }
                        if (typeof query.featurelimit == "number" && query.featurelimit < 2000) {
                            queryElem.setAttribute("featurelimit", query.featurelimit);
                        }
                        if (typeof query.subfields == "string" && query.subfields != "#ALL#") {
                            queryElem.setAttribute("subfields", query.subfields);
                        }
                        if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) {
                            queryElem.setAttribute("joinexpression", query.joinexpression);
                        }
                        if (typeof query.jointables == "string" && query.jointables.length > 0) {
                            queryElem.setAttribute("jointables", query.jointables);
                        }
                        ldef.appendChild(queryElem);
                    }
                    if (typeof props.layerlist[ld].renderer == "object") {
                        this.addRenderer(ldef, props.layerlist[ld].renderer);
                    }
                }
            }
        } else if (request.get_feature != null) {
            var getElem = this.createElementNS("", "GET_FEATURES");
            getElem.setAttribute("outputmode", "newxml");
            getElem.setAttribute("checkesc", "true");
            if (request.get_feature.geometry) {
                getElem.setAttribute("geometry", request.get_feature.geometry);
            } else {
                getElem.setAttribute("geometry", "false");
            }
            if (request.get_feature.compact) {
                getElem.setAttribute("compact", request.get_feature.compact);
            }
            if (request.get_feature.featurelimit == "number") {
                getElem.setAttribute("featurelimit", request.get_feature.featurelimit);
            }
            getElem.setAttribute("globalenvelope", "true");
            reqElem.appendChild(getElem);
            if (request.get_feature.layer != null && request.get_feature.layer.length > 0) {
                var lyrElem = this.createElementNS("", "LAYER");
                lyrElem.setAttribute("id", request.get_feature.layer);
                getElem.appendChild(lyrElem);
            }
            var fquery = request.get_feature.query;
            if (fquery != null) {
                var qElem = null;
                if (fquery.isspatial) {
                    qElem = this.createElementNS("", "SPATIALQUERY");
                } else {
                    qElem = this.createElementNS("", "QUERY");
                }
                getElem.appendChild(qElem);
                if (typeof fquery.accuracy == "number") {
                    qElem.setAttribute("accuracy", fquery.accuracy);
                }
                if (fquery.featurecoordsys != null) {
                    var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS");
                    if (fquery.featurecoordsys.id == 0) {
                        fcsElem1.setAttribute("string", fquery.featurecoordsys.string);
                    } else {
                        fcsElem1.setAttribute("id", fquery.featurecoordsys.id);
                    }
                    qElem.appendChild(fcsElem1);
                }
                if (fquery.filtercoordsys != null) {
                    var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS");
                    if (fquery.filtercoordsys.id === 0) {
                        fcsElem2.setAttribute("string", fquery.filtercoordsys.string);
                    } else {
                        fcsElem2.setAttribute("id", fquery.filtercoordsys.id);
                    }
                    qElem.appendChild(fcsElem2);
                }
                if (fquery.buffer > 0) {
                    var bufElem = this.createElementNS("", "BUFFER");
                    bufElem.setAttribute("distance", fquery.buffer);
                    qElem.appendChild(bufElem);
                }
                if (fquery.isspatial) {
                    var spfElem = this.createElementNS("", "SPATIALFILTER");
                    spfElem.setAttribute("relation", fquery.spatialfilter.relation);
                    qElem.appendChild(spfElem);
                    if (fquery.spatialfilter.envelope) {
                        var envElem = this.createElementNS("", "ENVELOPE");
                        envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx);
                        envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny);
                        envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx);
                        envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy);
                        spfElem.appendChild(envElem);
                    } else if (typeof fquery.spatialfilter.polygon == "object") {
                        spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon));
                    }
                }
                if (fquery.where != null && fquery.where.length > 0) {
                    qElem.setAttribute("where", fquery.where);
                }
            }
        }
        root.appendChild(reqElem);
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    addGroupRenderer: function (ldef, toprenderer) {
        var topRelem = this.createElementNS("", "GROUPRENDERER");
        ldef.appendChild(topRelem);
        for (var rind = 0; rind < toprenderer.length; rind++) {
            var renderer = toprenderer[rind];
            this.addRenderer(topRelem, renderer);
        }
    },
    addRenderer: function (topRelem, renderer) {
        if (OpenLayers.Util.isArray(renderer)) {
            this.addGroupRenderer(topRelem, renderer);
        } else {
            var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER");
            topRelem.appendChild(renderElem);
            if (renderElem.tagName == "VALUEMAPRENDERER") {
                this.addValueMapRenderer(renderElem, renderer);
            } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") {
                this.addValueMapLabelRenderer(renderElem, renderer);
            } else if (renderElem.tagName == "SIMPLELABELRENDERER") {
                this.addSimpleLabelRenderer(renderElem, renderer);
            } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") {
                this.addScaleDependentRenderer(renderElem, renderer);
            }
        }
    },
    addScaleDependentRenderer: function (renderElem, renderer) {
        if (typeof renderer.lower == "string" || typeof renderer.lower == "number") {
            renderElem.setAttribute("lower", renderer.lower);
        }
        if (typeof renderer.upper == "string" || typeof renderer.upper == "number") {
            renderElem.setAttribute("upper", renderer.upper);
        }
        this.addRenderer(renderElem, renderer.renderer);
    },
    addValueMapLabelRenderer: function (renderElem, renderer) {
        renderElem.setAttribute("lookupfield", renderer.lookupfield);
        renderElem.setAttribute("labelfield", renderer.labelfield);
        if (typeof renderer.exacts == "object") {
            for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {
                var exact = renderer.exacts[ext];
                var eelem = this.createElementNS("", "EXACT");
                if (typeof exact.value == "string") {
                    eelem.setAttribute("value", exact.value);
                }
                if (typeof exact.label == "string") {
                    eelem.setAttribute("label", exact.label);
                }
                if (typeof exact.method == "string") {
                    eelem.setAttribute("method", exact.method);
                }
                renderElem.appendChild(eelem);
                if (typeof exact.symbol == "object") {
                    var selem = null;
                    if (exact.symbol.type == "text") {
                        selem = this.createElementNS("", "TEXTSYMBOL");
                    }
                    if (selem != null) {
                        var keys = this.fontStyleKeys;
                        for (var i = 0, len = keys.length; i < len; i++) {
                            var key = keys[i];
                            if (exact.symbol[key]) {
                                selem.setAttribute(key, exact.symbol[key]);
                            }
                        }
                        eelem.appendChild(selem);
                    }
                }
            }
        }
    },
    addValueMapRenderer: function (renderElem, renderer) {
        renderElem.setAttribute("lookupfield", renderer.lookupfield);
        if (typeof renderer.ranges == "object") {
            for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) {
                var range = renderer.ranges[rng];
                var relem = this.createElementNS("", "RANGE");
                relem.setAttribute("lower", range.lower);
                relem.setAttribute("upper", range.upper);
                renderElem.appendChild(relem);
                if (typeof range.symbol == "object") {
                    var selem = null;
                    if (range.symbol.type == "simplepolygon") {
                        selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL");
                    }
                    if (selem != null) {
                        if (typeof range.symbol.boundarycolor == "string") {
                            selem.setAttribute("boundarycolor", range.symbol.boundarycolor);
                        }
                        if (typeof range.symbol.fillcolor == "string") {
                            selem.setAttribute("fillcolor", range.symbol.fillcolor);
                        }
                        if (typeof range.symbol.filltransparency == "number") {
                            selem.setAttribute("filltransparency", range.symbol.filltransparency);
                        }
                        relem.appendChild(selem);
                    }
                }
            }
        } else if (typeof renderer.exacts == "object") {
            for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) {
                var exact = renderer.exacts[ext];
                var eelem = this.createElementNS("", "EXACT");
                if (typeof exact.value == "string") {
                    eelem.setAttribute("value", exact.value);
                }
                if (typeof exact.label == "string") {
                    eelem.setAttribute("label", exact.label);
                }
                if (typeof exact.method == "string") {
                    eelem.setAttribute("method", exact.method);
                }
                renderElem.appendChild(eelem);
                if (typeof exact.symbol == "object") {
                    var selem = null;
                    if (exact.symbol.type == "simplemarker") {
                        selem = this.createElementNS("", "SIMPLEMARKERSYMBOL");
                    }
                    if (selem != null) {
                        if (typeof exact.symbol.antialiasing == "string") {
                            selem.setAttribute("antialiasing", exact.symbol.antialiasing);
                        }
                        if (typeof exact.symbol.color == "string") {
                            selem.setAttribute("color", exact.symbol.color);
                        }
                        if (typeof exact.symbol.outline == "string") {
                            selem.setAttribute("outline", exact.symbol.outline);
                        }
                        if (typeof exact.symbol.overlap == "string") {
                            selem.setAttribute("overlap", exact.symbol.overlap);
                        }
                        if (typeof exact.symbol.shadow == "string") {
                            selem.setAttribute("shadow", exact.symbol.shadow);
                        }
                        if (typeof exact.symbol.transparency == "number") {
                            selem.setAttribute("transparency", exact.symbol.transparency);
                        }
                        if (typeof exact.symbol.usecentroid == "string") {
                            selem.setAttribute("usecentroid", exact.symbol.usecentroid);
                        }
                        if (typeof exact.symbol.width == "number") {
                            selem.setAttribute("width", exact.symbol.width);
                        }
                        eelem.appendChild(selem);
                    }
                }
            }
        }
    },
    addSimpleLabelRenderer: function (renderElem, renderer) {
        renderElem.setAttribute("field", renderer.field);
        var keys = ['featureweight', 'howmanylabels', 'labelbufferratio', 'labelpriorities', 'labelweight', 'linelabelposition', 'rotationalangles'];
        for (var i = 0, len = keys.length; i < len; i++) {
            var key = keys[i];
            if (renderer[key]) {
                renderElem.setAttribute(key, renderer[key]);
            }
        }
        if (renderer.symbol.type == "text") {
            var symbol = renderer.symbol;
            var selem = this.createElementNS("", "TEXTSYMBOL");
            renderElem.appendChild(selem);
            var keys = this.fontStyleKeys;
            for (var i = 0, len = keys.length; i < len; i++) {
                var key = keys[i];
                if (symbol[key]) {
                    selem.setAttribute(key, renderer[key]);
                }
            }
        }
    },
    writePolygonGeometry: function (polygon) {
        if (!(polygon instanceof OpenLayers.Geometry.Polygon)) {
            throw {
                message: 'Cannot write polygon geometry to ArcXML with an ' + polygon.CLASS_NAME + ' object.',
                geometry: polygon
            };
        }
        var polyElem = this.createElementNS("", "POLYGON");
        for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) {
            var ring = polygon.components[ln];
            var ringElem = this.createElementNS("", "RING");
            for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) {
                var point = ring.components[rn];
                var pointElem = this.createElementNS("", "POINT");
                pointElem.setAttribute("x", point.x);
                pointElem.setAttribute("y", point.y);
                ringElem.appendChild(pointElem);
            }
            polyElem.appendChild(ringElem);
        }
        return polyElem;
    },
    parseResponse: function (data) {
        if (typeof data == "string") {
            var newData = new OpenLayers.Format.XML();
            data = newData.read(data);
        }
        var response = new OpenLayers.Format.ArcXML.Response();
        var errorNode = data.getElementsByTagName("ERROR");
        if (errorNode != null && errorNode.length > 0) {
            response.error = this.getChildValue(errorNode, "Unknown error.");
        } else {
            var responseNode = data.getElementsByTagName("RESPONSE");
            if (responseNode == null || responseNode.length == 0) {
                response.error = "No RESPONSE tag found in ArcXML response.";
                return response;
            }
            var rtype = responseNode[0].firstChild.nodeName;
            if (rtype == "#text") {
                rtype = responseNode[0].firstChild.nextSibling.nodeName;
            }
            if (rtype == "IMAGE") {
                var envelopeNode = data.getElementsByTagName("ENVELOPE");
                var outputNode = data.getElementsByTagName("OUTPUT");
                if (envelopeNode == null || envelopeNode.length == 0) {
                    response.error = "No ENVELOPE tag found in ArcXML response.";
                } else if (outputNode == null || outputNode.length == 0) {
                    response.error = "No OUTPUT tag found in ArcXML response.";
                } else {
                    var envAttr = this.parseAttributes(envelopeNode[0]);
                    var outputAttr = this.parseAttributes(outputNode[0]);
                    if (typeof outputAttr.type == "string") {
                        response.image = {
                            envelope: envAttr,
                            output: {
                                type: outputAttr.type,
                                data: this.getChildValue(outputNode[0])
                            }
                        };
                    } else {
                        response.image = {
                            envelope: envAttr,
                            output: outputAttr
                        };
                    }
                }
            } else if (rtype == "FEATURES") {
                var features = responseNode[0].getElementsByTagName("FEATURES");
                var featureCount = features[0].getElementsByTagName("FEATURECOUNT");
                response.features.featurecount = featureCount[0].getAttribute("count");
                if (response.features.featurecount > 0) {
                    var envelope = features[0].getElementsByTagName("ENVELOPE");
                    response.features.envelope = this.parseAttributes(envelope[0], typeof (0));
                    var featureList = features[0].getElementsByTagName("FEATURE");
                    for (var fn = 0; fn < featureList.length; fn++) {
                        var feature = new OpenLayers.Feature.Vector();
                        var fields = featureList[fn].getElementsByTagName("FIELD");
                        for (var fdn = 0; fdn < fields.length; fdn++) {
                            var fieldName = fields[fdn].getAttribute("name");
                            var fieldValue = fields[fdn].getAttribute("value");
                            feature.attributes[fieldName] = fieldValue;
                        }
                        var geom = featureList[fn].getElementsByTagName("POLYGON");
                        if (geom.length > 0) {
                            var ring = geom[0].getElementsByTagName("RING");
                            var polys = [];
                            for (var rn = 0; rn < ring.length; rn++) {
                                var linearRings = [];
                                linearRings.push(this.parsePointGeometry(ring[rn]));
                                var holes = ring[rn].getElementsByTagName("HOLE");
                                for (var hn = 0; hn < holes.length; hn++) {
                                    linearRings.push(this.parsePointGeometry(holes[hn]));
                                }
                                holes = null;
                                polys.push(new OpenLayers.Geometry.Polygon(linearRings));
                                linearRings = null;
                            }
                            ring = null;
                            if (polys.length == 1) {
                                feature.geometry = polys[0];
                            } else {
                                feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys);
                            }
                        }
                        response.features.feature.push(feature);
                    }
                }
            } else {
                response.error = "Unidentified response type.";
            }
        }
        return response;
    },
    parseAttributes: function (node, type) {
        var attributes = {};
        for (var attr = 0; attr < node.attributes.length; attr++) {
            if (type == "number") {
                attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue);
            } else {
                attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue;
            }
        }
        return attributes;
    },
    parsePointGeometry: function (node) {
        var ringPoints = [];
        var coords = node.getElementsByTagName("COORDS");
        if (coords.length > 0) {
            var coordArr = this.getChildValue(coords[0]);
            coordArr = coordArr.split(/;/);
            for (var cn = 0; cn < coordArr.length; cn++) {
                var coordItems = coordArr[cn].split(/ /);
                ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(coordItems[0]), parseFloat(coordItems[1])));
            }
            coords = null;
        } else {
            var point = node.getElementsByTagName("POINT");
            if (point.length > 0) {
                for (var pn = 0; pn < point.length; pn++) {
                    ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(point[pn].getAttribute("x")), parseFloat(point[pn].getAttribute("y"))));
                }
            }
            point = null;
        }
        return new OpenLayers.Geometry.LinearRing(ringPoints);
    },
    CLASS_NAME: "OpenLayers.Format.ArcXML"
});
OpenLayers.Format.ArcXML.Request = OpenLayers.Class({
    initialize: function (params) {
        var defaults = {
            get_image: {
                properties: {
                    background: null,
                    draw: true,
                    envelope: {
                        minx: 0,
                        miny: 0,
                        maxx: 0,
                        maxy: 0
                    },
                    featurecoordsys: {
                        id: 0,
                        string: "",
                        datumtransformid: 0,
                        datumtransformstring: ""
                    },
                    filtercoordsys: {
                        id: 0,
                        string: "",
                        datumtransformid: 0,
                        datumtransformstring: ""
                    },
                    imagesize: {
                        height: 0,
                        width: 0,
                        dpi: 96,
                        printheight: 0,
                        printwidth: 0,
                        scalesymbols: false
                    },
                    layerlist: [],
                    output: {
                        baseurl: "",
                        legendbaseurl: "",
                        legendname: "",
                        legendpath: "",
                        legendurl: "",
                        name: "",
                        path: "",
                        type: "jpg",
                        url: ""
                    }
                }
            },
            get_feature: {
                layer: "",
                query: {
                    isspatial: false,
                    featurecoordsys: {
                        id: 0,
                        string: "",
                        datumtransformid: 0,
                        datumtransformstring: ""
                    },
                    filtercoordsys: {
                        id: 0,
                        string: "",
                        datumtransformid: 0,
                        datumtransformstring: ""
                    },
                    buffer: 0,
                    where: "",
                    spatialfilter: {
                        relation: "envelope_intersection",
                        envelope: null
                    }
                }
            },
            environment: {
                separators: {
                    cs: " ",
                    ts: ";"
                }
            },
            layer: [],
            workspaces: []
        };
        return OpenLayers.Util.extend(this, defaults);
    },
    CLASS_NAME: "OpenLayers.Format.ArcXML.Request"
});
OpenLayers.Format.ArcXML.Response = OpenLayers.Class({
    initialize: function (params) {
        var defaults = {
            image: {
                envelope: null,
                output: ''
            },
            features: {
                featurecount: 0,
                envelope: null,
                feature: []
            },
            error: ''
        };
        return OpenLayers.Util.extend(this, defaults);
    },
    CLASS_NAME: "OpenLayers.Format.ArcXML.Response"
});
OpenLayers.Request = {
    DEFAULT_CONFIG: {
        method: "GET",
        url: window.location.href,
        async: true,
        user: undefined,
        password: undefined,
        params: null,
        proxy: OpenLayers.ProxyHost,
        headers: {},
        data: null,
        callback: function () {},
        success: null,
        failure: null,
        scope: null
    },
    URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/,
    events: new OpenLayers.Events(this, null, ["complete", "success", "failure"]),
    issue: function (config) {
        var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, {
            proxy: OpenLayers.ProxyHost
        });
        config = OpenLayers.Util.applyDefaults(config, defaultConfig);
        var request = new OpenLayers.Request.XMLHttpRequest();
        var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {}));
        var sameOrigin = !(url.indexOf("http") == 0);
        var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX);
        if (urlParts) {
            var location = window.location;
            sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname;
            var uPort = urlParts[4],
                lPort = location.port;
            if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") {
                sameOrigin = sameOrigin && uPort == lPort;
            }
        }
        if (!sameOrigin) {
            if (config.proxy) {
                if (typeof config.proxy == "function") {
                    url = config.proxy(url);
                } else {
                    url = config.proxy + encodeURIComponent(url);
                }
            } else {
                OpenLayers.Console.warn(OpenLayers.i18n("proxyNeeded"), {
                    url: url
                });
            }
        }
        request.open(config.method, url, config.async, config.user, config.password);
        for (var header in config.headers) {
            request.setRequestHeader(header, config.headers[header]);
        }
        var events = this.events;
        var self = this;
        request.onreadystatechange = function () {
            if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) {
                var proceed = events.triggerEvent("complete", {
                    request: request,
                    config: config,
                    requestUrl: url
                });
                if (proceed !== false) {
                    self.runCallbacks({
                        request: request,
                        config: config,
                        requestUrl: url
                    });
                }
            }
        };
        if (config.async === false) {
            request.send(config.data);
        } else {
            window.setTimeout(function () {
                if (request.readyState !== 0) {
                    request.send(config.data);
                }
            }, 0);
        }
        return request;
    },
    runCallbacks: function (options) {
        var request = options.request;
        var config = options.config;
        var complete = (config.scope) ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback;
        var success;
        if (config.success) {
            success = (config.scope) ? OpenLayers.Function.bind(config.success, config.scope) : config.success;
        }
        var failure;
        if (config.failure) {
            failure = (config.scope) ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure;
        }
        if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && request.responseText) {
            request.status = 200;
        }
        complete(request);
        if (!request.status || (request.status >= 200 && request.status < 300)) {
            this.events.triggerEvent("success", options);
            if (success) {
                success(request);
            }
        }
        if (request.status && (request.status < 200 || request.status >= 300)) {
            this.events.triggerEvent("failure", options);
            if (failure) {
                failure(request);
            }
        }
    },
    GET: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "GET"
        });
        return OpenLayers.Request.issue(config);
    },
    POST: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "POST"
        });
        config.headers = config.headers ? config.headers : {};
        if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
            config.headers["Content-Type"] = "application/xml";
        }
        return OpenLayers.Request.issue(config);
    },
    PUT: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "PUT"
        });
        config.headers = config.headers ? config.headers : {};
        if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) {
            config.headers["Content-Type"] = "application/xml";
        }
        return OpenLayers.Request.issue(config);
    },
    DELETE: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "DELETE"
        });
        return OpenLayers.Request.issue(config);
    },
    HEAD: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "HEAD"
        });
        return OpenLayers.Request.issue(config);
    },
    OPTIONS: function (config) {
        config = OpenLayers.Util.extend(config, {
            method: "OPTIONS"
        });
        return OpenLayers.Request.issue(config);
    }
};
OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
    DEFAULT_PARAMS: {
        ClientVersion: "9.2",
        ServiceName: ''
    },
    tileSize: null,
    featureCoordSys: "4326",
    filterCoordSys: "4326",
    layers: null,
    async: true,
    name: "ArcIMS",
    isBaseLayer: true,
    DEFAULT_OPTIONS: {
        tileSize: new OpenLayers.Size(512, 512),
        featureCoordSys: "4326",
        filterCoordSys: "4326",
        layers: null,
        isBaseLayer: true,
        async: true,
        name: "ArcIMS"
    },
    initialize: function (name, url, options) {
        this.tileSize = new OpenLayers.Size(512, 512);
        this.params = OpenLayers.Util.applyDefaults({
            ServiceName: options.serviceName
        }, this.DEFAULT_PARAMS);
        this.options = OpenLayers.Util.applyDefaults(options, this.DEFAULT_OPTIONS);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, this.params, options]);
        if (this.transparent) {
            if (!this.isBaseLayer) {
                this.isBaseLayer = false;
            }
            if (this.format == "image/jpeg") {
                this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png";
            }
        }
        if (this.options.layers === null) {
            this.options.layers = [];
        }
    },
    destroy: function () {
        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
    },
    getURL: function (bounds) {
        var url = "";
        bounds = this.adjustBounds(bounds);
        var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {
            requesttype: "image",
            envelope: bounds.toArray(),
            tileSize: this.tileSize
        }));
        var req = new OpenLayers.Request.POST({
            url: this.getFullRequestString(),
            data: axlReq.write(),
            async: false
        });
        if (req != null) {
            var doc = req.responseXML;
            if (!doc || !doc.documentElement) {
                doc = req.responseText;
            }
            var axlResp = new OpenLayers.Format.ArcXML();
            var arcxml = axlResp.read(doc);
            url = this.getUrlOrImage(arcxml.image.output);
        }
        return url;
    },
    getURLasync: function (bounds, scope, prop, callback) {
        bounds = this.adjustBounds(bounds);
        var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, {
            requesttype: "image",
            envelope: bounds.toArray(),
            tileSize: this.tileSize
        }));
        OpenLayers.Request.POST({
            url: this.getFullRequestString(),
            async: true,
            data: axlReq.write(),
            callback: function (req) {
                var doc = req.responseXML;
                if (!doc || !doc.documentElement) {
                    doc = req.responseText;
                }
                var axlResp = new OpenLayers.Format.ArcXML();
                var arcxml = axlResp.read(doc);
                scope[prop] = this.getUrlOrImage(arcxml.image.output);
                callback.apply(scope);
            },
            scope: this
        });
    },
    getUrlOrImage: function (output) {
        var ret = "";
        if (output.url) {
            ret = output.url;
        } else if (output.data) {
            ret = "data:image/" + output.type + ";base64," + output.data;
        }
        return ret;
    },
    setLayerQuery: function (id, querydef) {
        for (var lyr = 0; lyr < this.options.layers.length; lyr++) {
            if (id == this.options.layers[lyr].id) {
                this.options.layers[lyr].query = querydef;
                return;
            }
        }
        this.options.layers.push({
            id: id,
            visible: true,
            query: querydef
        });
    },
    getFeatureInfo: function (geometry, layer, options) {
        var buffer = options.buffer || 1;
        var callback = options.callback ||
        function () {};
        var scope = options.scope || window;
        var requestOptions = {};
        OpenLayers.Util.extend(requestOptions, this.options);
        requestOptions.requesttype = "feature";
        if (geometry instanceof OpenLayers.LonLat) {
            requestOptions.polygon = null;
            requestOptions.envelope = [geometry.lon - buffer, geometry.lat - buffer, geometry.lon + buffer, geometry.lat + buffer];
        } else if (geometry instanceof OpenLayers.Geometry.Polygon) {
            requestOptions.envelope = null;
            requestOptions.polygon = geometry;
        }
        var arcxml = new OpenLayers.Format.ArcXML(requestOptions);
        OpenLayers.Util.extend(arcxml.request.get_feature, options);
        arcxml.request.get_feature.layer = layer.id;
        if (typeof layer.query.accuracy == "number") {
            arcxml.request.get_feature.query.accuracy = layer.query.accuracy;
        } else {
            var mapCenter = this.map.getCenter();
            var viewPx = this.map.getViewPortPxFromLonLat(mapCenter);
            viewPx.x++;
            var mapOffCenter = this.map.getLonLatFromPixel(viewPx);
            arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon;
        }
        arcxml.request.get_feature.query.where = layer.query.where;
        arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection";
        OpenLayers.Request.POST({
            url: this.getFullRequestString({
                'CustomService': 'Query'
            }),
            data: arcxml.write(),
            callback: function (request) {
                var response = arcxml.parseResponse(request.responseText);
                if (!arcxml.iserror()) {
                    callback.call(scope, response.features);
                } else {
                    callback.call(scope, null);
                }
            }
        });
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.ArcIMS(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.ArcIMS"
});
OpenLayers.Format.OWSCommon.v1_1_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1, {
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        xlink: "http://www.w3.org/1999/xlink"
    },
    readers: {
        "ows": OpenLayers.Util.applyDefaults({
            "ExceptionReport": function (node, obj) {
                obj.exceptionReport = {
                    version: node.getAttribute('version'),
                    language: node.getAttribute('xml:lang'),
                    exceptions: []
                };
                this.readChildNodes(node, obj.exceptionReport);
            },
            "AllowedValues": function (node, parameter) {
                parameter.allowedValues = {};
                this.readChildNodes(node, parameter.allowedValues);
            },
            "AnyValue": function (node, parameter) {
                parameter.anyValue = true;
            },
            "DataType": function (node, parameter) {
                parameter.dataType = this.getChildValue(node);
            },
            "Range": function (node, allowedValues) {
                allowedValues.range = {};
                this.readChildNodes(node, allowedValues.range);
            },
            "MinimumValue": function (node, range) {
                range.minValue = this.getChildValue(node);
            },
            "MaximumValue": function (node, range) {
                range.maxValue = this.getChildValue(node);
            },
            "Identifier": function (node, obj) {
                obj.identifier = this.getChildValue(node);
            },
            "SupportedCRS": function (node, obj) {
                obj.supportedCRS = this.getChildValue(node);
            }
        }, OpenLayers.Format.OWSCommon.v1.prototype.readers["ows"])
    },
    writers: {
        "ows": OpenLayers.Util.applyDefaults({
            "Range": function (range) {
                var node = this.createElementNSPlus("ows:Range", {
                    attributes: {
                        'ows:rangeClosure': range.closure
                    }
                });
                this.writeNode("ows:MinimumValue", range.minValue, node);
                this.writeNode("ows:MaximumValue", range.maxValue, node);
                return node;
            },
            "MinimumValue": function (minValue) {
                var node = this.createElementNSPlus("ows:MinimumValue", {
                    value: minValue
                });
                return node;
            },
            "MaximumValue": function (maxValue) {
                var node = this.createElementNSPlus("ows:MaximumValue", {
                    value: maxValue
                });
                return node;
            },
            "Value": function (value) {
                var node = this.createElementNSPlus("ows:Value", {
                    value: value
                });
                return node;
            }
        }, OpenLayers.Format.OWSCommon.v1.prototype.writers["ows"])
    },
    CLASS_NAME: "OpenLayers.Format.OWSCommon.v1_1_0"
});
OpenLayers.Format.WCSGetCoverage = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        wcs: "http://www.opengis.net/wcs/1.1",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    VERSION: "1.1.2",
    schemaLocation: "http://www.opengis.net/wcs/1.1 http://schemas.opengis.net/wcs/1.1/wcsGetCoverage.xsd",
    write: function (options) {
        var node = this.writeNode("wcs:GetCoverage", options);
        this.setAttributeNS(node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    writers: {
        "wcs": {
            "GetCoverage": function (options) {
                var node = this.createElementNSPlus("wcs:GetCoverage", {
                    attributes: {
                        version: options.version || this.VERSION,
                        service: 'WCS'
                    }
                });
                this.writeNode("ows:Identifier", options.identifier, node);
                this.writeNode("wcs:DomainSubset", options.domainSubset, node);
                this.writeNode("wcs:Output", options.output, node);
                return node;
            },
            "DomainSubset": function (domainSubset) {
                var node = this.createElementNSPlus("wcs:DomainSubset", {});
                this.writeNode("ows:BoundingBox", domainSubset.boundingBox, node);
                if (domainSubset.temporalSubset) {
                    this.writeNode("wcs:TemporalSubset", domainSubset.temporalSubset, node);
                }
                return node;
            },
            "TemporalSubset": function (temporalSubset) {
                var node = this.createElementNSPlus("wcs:TemporalSubset", {});
                for (var i = 0, len = temporalSubset.timePeriods.length; i < len; ++i) {
                    this.writeNode("wcs:TimePeriod", temporalSubset.timePeriods[i], node);
                }
                return node;
            },
            "TimePeriod": function (timePeriod) {
                var node = this.createElementNSPlus("wcs:TimePeriod", {});
                this.writeNode("wcs:BeginPosition", timePeriod.begin, node);
                this.writeNode("wcs:EndPosition", timePeriod.end, node);
                if (timePeriod.resolution) {
                    this.writeNode("wcs:TimeResolution", timePeriod.resolution, node);
                }
                return node;
            },
            "BeginPosition": function (begin) {
                var node = this.createElementNSPlus("wcs:BeginPosition", {
                    value: begin
                });
                return node;
            },
            "EndPosition": function (end) {
                var node = this.createElementNSPlus("wcs:EndPosition", {
                    value: end
                });
                return node;
            },
            "TimeResolution": function (resolution) {
                var node = this.createElementNSPlus("wcs:TimeResolution", {
                    value: resolution
                });
                return node;
            },
            "Output": function (output) {
                var node = this.createElementNSPlus("wcs:Output", {
                    attributes: {
                        format: output.format,
                        store: output.store
                    }
                });
                if (output.gridCRS) {
                    this.writeNode("wcs:GridCRS", output.gridCRS, node);
                }
                return node;
            },
            "GridCRS": function (gridCRS) {
                var node = this.createElementNSPlus("wcs:GridCRS", {});
                this.writeNode("wcs:GridBaseCRS", gridCRS.baseCRS, node);
                if (gridCRS.type) {
                    this.writeNode("wcs:GridType", gridCRS.type, node);
                }
                if (gridCRS.origin) {
                    this.writeNode("wcs:GridOrigin", gridCRS.origin, node);
                }
                this.writeNode("wcs:GridOffsets", gridCRS.offsets, node);
                if (gridCRS.CS) {
                    this.writeNode("wcs:GridCS", gridCRS.CS, node);
                }
                return node;
            },
            "GridBaseCRS": function (baseCRS) {
                return this.createElementNSPlus("wcs:GridBaseCRS", {
                    value: baseCRS
                });
            },
            "GridOrigin": function (origin) {
                return this.createElementNSPlus("wcs:GridOrigin", {
                    value: origin
                });
            },
            "GridType": function (type) {
                return this.createElementNSPlus("wcs:GridType", {
                    value: type
                });
            },
            "GridOffsets": function (offsets) {
                return this.createElementNSPlus("wcs:GridOffsets", {
                    value: offsets
                });
            },
            "GridCS": function (CS) {
                return this.createElementNSPlus("wcs:GridCS", {
                    value: CS
                });
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows
    },
    CLASS_NAME: "OpenLayers.Format.WCSGetCoverage"
});
OpenLayers.Format.WPSExecute = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        gml: "http://www.opengis.net/gml",
        wps: "http://www.opengis.net/wps/1.0.0",
        wfs: "http://www.opengis.net/wfs",
        ogc: "http://www.opengis.net/ogc",
        wcs: "http://www.opengis.net/wcs",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    VERSION: "1.0.0",
    schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",
    schemaLocationAttr: function (options) {
        return undefined;
    },
    write: function (options) {
        var doc;
        if (window.ActiveXObject) {
            doc = new ActiveXObject("Microsoft.XMLDOM");
            this.xmldom = doc;
        } else {
            doc = document.implementation.createDocument("", "", null);
        }
        var node = this.writeNode("wps:Execute", options, doc);
        this.setAttributeNS(node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    writers: {
        "wps": {
            "Execute": function (options) {
                var node = this.createElementNSPlus("wps:Execute", {
                    attributes: {
                        version: this.VERSION,
                        service: 'WPS'
                    }
                });
                this.writeNode("ows:Identifier", options.identifier, node);
                this.writeNode("wps:DataInputs", options.dataInputs, node);
                this.writeNode("wps:ResponseForm", options.responseForm, node);
                return node;
            },
            "ResponseForm": function (responseForm) {
                var node = this.createElementNSPlus("wps:ResponseForm", {});
                if (responseForm.rawDataOutput) {
                    this.writeNode("wps:RawDataOutput", responseForm.rawDataOutput, node);
                }
                if (responseForm.responseDocument) {
                    this.writeNode("wps:ResponseDocument", responseForm.responseDocument, node);
                }
                return node;
            },
            "ResponseDocument": function (responseDocument) {
                var node = this.createElementNSPlus("wps:ResponseDocument", {
                    attributes: {
                        storeExecuteResponse: responseDocument.storeExecuteResponse,
                        lineage: responseDocument.lineage,
                        status: responseDocument.status
                    }
                });
                if (responseDocument.output) {
                    this.writeNode("wps:Output", responseDocument.output, node);
                }
                return node;
            },
            "Output": function (output) {
                var node = this.createElementNSPlus("wps:Output", {
                    attributes: {
                        asReference: output.asReference
                    }
                });
                this.writeNode("ows:Identifier", output.identifier, node);
                this.writeNode("ows:Title", output.title, node);
                this.writeNode("ows:Abstract", output["abstract"], node);
                return node;
            },
            "RawDataOutput": function (rawDataOutput) {
                var node = this.createElementNSPlus("wps:RawDataOutput", {
                    attributes: {
                        mimeType: rawDataOutput.mimeType
                    }
                });
                this.writeNode("ows:Identifier", rawDataOutput.identifier, node);
                return node;
            },
            "DataInputs": function (dataInputs) {
                var node = this.createElementNSPlus("wps:DataInputs", {});
                for (var i = 0, ii = dataInputs.length; i < ii; ++i) {
                    this.writeNode("wps:Input", dataInputs[i], node);
                }
                return node;
            },
            "Input": function (input) {
                var node = this.createElementNSPlus("wps:Input", {});
                this.writeNode("ows:Identifier", input.identifier, node);
                if (input.title) {
                    this.writeNode("ows:Title", input.title, node);
                }
                if (input.data) {
                    this.writeNode("wps:Data", input.data, node);
                }
                if (input.reference) {
                    this.writeNode("wps:Reference", input.reference, node);
                }
                return node;
            },
            "Data": function (data) {
                var node = this.createElementNSPlus("wps:Data", {});
                if (data.literalData) {
                    this.writeNode("wps:LiteralData", data.literalData, node);
                } else if (data.complexData) {
                    this.writeNode("wps:ComplexData", data.complexData, node);
                }
                return node;
            },
            "LiteralData": function (literalData) {
                var node = this.createElementNSPlus("wps:LiteralData", {
                    attributes: {
                        uom: literalData.uom
                    },
                    value: literalData.value
                });
                return node;
            },
            "ComplexData": function (complexData) {
                var node = this.createElementNSPlus("wps:ComplexData", {
                    attributes: {
                        mimeType: complexData.mimeType,
                        encoding: complexData.encoding,
                        schema: complexData.schema
                    }
                });
                node.appendChild(this.getXMLDoc().createCDATASection(complexData.value));
                return node;
            },
            "Reference": function (reference) {
                var node = this.createElementNSPlus("wps:Reference", {
                    attributes: {
                        mimeType: reference.mimeType,
                        "xlink:href": reference.href,
                        method: reference.method,
                        encoding: reference.encoding,
                        schema: reference.schema
                    }
                });
                if (reference.body) {
                    this.writeNode("wps:Body", reference.body, node);
                }
                return node;
            },
            "Body": function (body) {
                var node = this.createElementNSPlus("wps:Body", {});
                if (body.wcs) {
                    this.writeNode("wcs:GetCoverage", body.wcs, node);
                } else if (body.wfs) {
                    this.featureType = body.wfs.featureType;
                    this.version = body.wfs.version;
                    this.writeNode("wfs:GetFeature", body.wfs, node);
                } else {
                    this.writeNode("wps:Execute", body, node);
                }
                return node;
            }
        },
        "wcs": OpenLayers.Format.WCSGetCoverage.prototype.writers.wcs,
        "wfs": OpenLayers.Format.WFST.v1_1_0.prototype.writers.wfs,
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.writers.ows
    },
    CLASS_NAME: "OpenLayers.Format.WPSExecute"
});
OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, {
    slideFactor: 50,
    slideRatio: null,
    buttons: null,
    position: null,
    initialize: function (options) {
        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, OpenLayers.Control.PanZoom.Y);
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },
    destroy: function () {
        this.removeButtons();
        this.buttons = null;
        this.position = null;
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    draw: function (px) {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        px = this.position;
        this.buttons = [];
        var sz = new OpenLayers.Size(18, 18);
        var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);
        this._addButton("panup", "north-mini.png", centered, sz);
        px.y = centered.y + sz.h;
        this._addButton("panleft", "west-mini.png", px, sz);
        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
        this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz);
        this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz);
        this._addButton("zoomworld", "zoom-world-mini.png", centered.add(0, sz.h * 4 + 5), sz);
        this._addButton("zoomout", "zoom-minus-mini.png", centered.add(0, sz.h * 5 + 5), sz);
        return this.div;
    },
    _addButton: function (id, img, xy, sz) {
        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
        var btn = OpenLayers.Util.createAlphaImageDiv(this.id + "_" + id, xy, sz, imgLocation, "absolute");
        btn.style.cursor = "pointer";
        this.div.appendChild(btn);
        OpenLayers.Event.observe(btn, "mousedown", OpenLayers.Function.bindAsEventListener(this.buttonDown, btn));
        OpenLayers.Event.observe(btn, "dblclick", OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
        OpenLayers.Event.observe(btn, "click", OpenLayers.Function.bindAsEventListener(this.doubleClick, btn));
        btn.action = id;
        btn.map = this.map;
        if (!this.slideRatio) {
            var slideFactorPixels = this.slideFactor;
            var getSlideFactor = function () {
                    return slideFactorPixels;
                };
        } else {
            var slideRatio = this.slideRatio;
            var getSlideFactor = function (dim) {
                    return this.map.getSize()[dim] * slideRatio;
                };
        }
        btn.getSlideFactor = getSlideFactor;
        this.buttons.push(btn);
        return btn;
    },
    _removeButton: function (btn) {
        OpenLayers.Event.stopObservingElement(btn);
        btn.map = null;
        btn.getSlideFactor = null;
        this.div.removeChild(btn);
        OpenLayers.Util.removeItem(this.buttons, btn);
    },
    removeButtons: function () {
        for (var i = this.buttons.length - 1; i >= 0; --i) {
            this._removeButton(this.buttons[i]);
        }
    },
    doubleClick: function (evt) {
        OpenLayers.Event.stop(evt);
        return false;
    },
    buttonDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        switch (this.action) {
        case "panup":
            this.map.pan(0, -this.getSlideFactor("h"));
            break;
        case "pandown":
            this.map.pan(0, this.getSlideFactor("h"));
            break;
        case "panleft":
            this.map.pan(-this.getSlideFactor("w"), 0);
            break;
        case "panright":
            this.map.pan(this.getSlideFactor("w"), 0);
            break;
        case "zoomin":
            this.map.zoomIn();
            break;
        case "zoomout":
            this.map.zoomOut();
            break;
        case "zoomworld":
            this.map.zoomToMaxExtent();
            break;
        }
        OpenLayers.Event.stop(evt);
    },
    CLASS_NAME: "OpenLayers.Control.PanZoom"
});
OpenLayers.Control.PanZoom.X = 4;
OpenLayers.Control.PanZoom.Y = 4;
OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, {
    zoomStopWidth: 18,
    zoomStopHeight: 11,
    slider: null,
    sliderEvents: null,
    zoombarDiv: null,
    divEvents: null,
    zoomWorldIcon: false,
    panIcons: true,
    forceFixedZoomLevel: false,
    mouseDragStart: null,
    deltaY: null,
    zoomStart: null,
    destroy: function () {
        this._removeZoomBar();
        this.map.events.un({
            "changebaselayer": this.redraw,
            scope: this
        });
        OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
        delete this.mouseDragStart;
        delete this.zoomStart;
    },
    setMap: function (map) {
        OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments);
        this.map.events.register("changebaselayer", this, this.redraw);
    },
    redraw: function () {
        if (this.div != null) {
            this.removeButtons();
            this._removeZoomBar();
        }
        this.draw();
    },
    draw: function (px) {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        px = this.position.clone();
        this.buttons = [];
        var sz = new OpenLayers.Size(18, 18);
        if (this.panIcons) {
            var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y);
            var wposition = sz.w;
            if (this.zoomWorldIcon) {
                centered = new OpenLayers.Pixel(px.x + sz.w, px.y);
            }
            this._addButton("panup", "north-mini.png", centered, sz);
            px.y = centered.y + sz.h;
            this._addButton("panleft", "west-mini.png", px, sz);
            if (this.zoomWorldIcon) {
                this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
                wposition *= 2;
            }
            this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
            this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz);
            this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz);
            centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5));
            this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
        } else {
            this._addButton("zoomin", "zoom-plus-mini.png", px, sz);
            centered = this._addZoomBar(px.add(0, sz.h));
            this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
            if (this.zoomWorldIcon) {
                centered = centered.add(0, sz.h + 3);
                this._addButton("zoomworld", "zoom-world-mini.png", centered, sz);
            }
        }
        return this.div;
    },
    _addZoomBar: function (centered) {
        var imgLocation = OpenLayers.Util.getImagesLocation();
        var id = this.id + "_" + this.map.id;
        var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom();
        var slider = OpenLayers.Util.createAlphaImageDiv(id, centered.add(-1, zoomsToEnd * this.zoomStopHeight), new OpenLayers.Size(20, 9), imgLocation + "slider.png", "absolute");
        slider.style.cursor = "move";
        this.slider = slider;
        this.sliderEvents = new OpenLayers.Events(this, slider, null, true, {
            includeXY: true
        });
        this.sliderEvents.on({
            "touchstart": this.zoomBarDown,
            "touchmove": this.zoomBarDrag,
            "touchend": this.zoomBarUp,
            "mousedown": this.zoomBarDown,
            "mousemove": this.zoomBarDrag,
            "mouseup": this.zoomBarUp,
            "dblclick": this.doubleClick,
            "click": this.doubleClick
        });
        var sz = new OpenLayers.Size();
        sz.h = this.zoomStopHeight * this.map.getNumZoomLevels();
        sz.w = this.zoomStopWidth;
        var div = null;
        if (OpenLayers.Util.alphaHack()) {
            var id = this.id + "_" + this.map.id;
            div = OpenLayers.Util.createAlphaImageDiv(id, centered, new OpenLayers.Size(sz.w, this.zoomStopHeight), imgLocation + "zoombar.png", "absolute", null, "crop");
            div.style.height = sz.h + "px";
        } else {
            div = OpenLayers.Util.createDiv('OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id, centered, sz, imgLocation + "zoombar.png");
        }
        div.style.cursor = "pointer";
        this.zoombarDiv = div;
        this.divEvents = new OpenLayers.Events(this, div, null, true, {
            includeXY: true
        });
        this.divEvents.on({
            "touchmove": this.passEventToSlider,
            "mousedown": this.divClick,
            "mousemove": this.passEventToSlider,
            "dblclick": this.doubleClick,
            "click": this.doubleClick
        });
        this.div.appendChild(div);
        this.startTop = parseInt(div.style.top);
        this.div.appendChild(slider);
        this.map.events.register("zoomend", this, this.moveZoomBar);
        centered = centered.add(0, this.zoomStopHeight * this.map.getNumZoomLevels());
        return centered;
    },
    _removeZoomBar: function () {
        this.sliderEvents.un({
            "touchmove": this.zoomBarDrag,
            "mousedown": this.zoomBarDown,
            "mousemove": this.zoomBarDrag,
            "mouseup": this.zoomBarUp,
            "dblclick": this.doubleClick,
            "click": this.doubleClick
        });
        this.sliderEvents.destroy();
        this.divEvents.un({
            "touchmove": this.passEventToSlider,
            "mousedown": this.divClick,
            "mousemove": this.passEventToSlider,
            "dblclick": this.doubleClick,
            "click": this.doubleClick
        });
        this.divEvents.destroy();
        this.div.removeChild(this.zoombarDiv);
        this.zoombarDiv = null;
        this.div.removeChild(this.slider);
        this.slider = null;
        this.map.events.unregister("zoomend", this, this.moveZoomBar);
    },
    passEventToSlider: function (evt) {
        this.sliderEvents.handleBrowserEvent(evt);
    },
    divClick: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        var levels = evt.xy.y / this.zoomStopHeight;
        if (this.forceFixedZoomLevel || !this.map.fractionalZoom) {
            levels = Math.floor(levels);
        }
        var zoom = (this.map.getNumZoomLevels() - 1) - levels;
        zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1);
        this.map.zoomTo(zoom);
        OpenLayers.Event.stop(evt);
    },
    zoomBarDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) {
            return;
        }
        this.map.events.on({
            "touchmove": this.passEventToSlider,
            "mousemove": this.passEventToSlider,
            "mouseup": this.passEventToSlider,
            scope: this
        });
        this.mouseDragStart = evt.xy.clone();
        this.zoomStart = evt.xy.clone();
        this.div.style.cursor = "move";
        this.zoombarDiv.offsets = null;
        OpenLayers.Event.stop(evt);
    },
    zoomBarDrag: function (evt) {
        if (this.mouseDragStart != null) {
            var deltaY = this.mouseDragStart.y - evt.xy.y;
            var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv);
            if ((evt.clientY - offsets[1]) > 0 && (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) {
                var newTop = parseInt(this.slider.style.top) - deltaY;
                this.slider.style.top = newTop + "px";
                this.mouseDragStart = evt.xy.clone();
            }
            this.deltaY = this.zoomStart.y - evt.xy.y;
            OpenLayers.Event.stop(evt);
        }
    },
    zoomBarUp: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") {
            return;
        }
        if (this.mouseDragStart) {
            this.div.style.cursor = "";
            this.map.events.un({
                "touchmove": this.passEventToSlider,
                "mouseup": this.passEventToSlider,
                "mousemove": this.passEventToSlider,
                scope: this
            });
            var zoomLevel = this.map.zoom;
            if (!this.forceFixedZoomLevel && this.map.fractionalZoom) {
                zoomLevel += this.deltaY / this.zoomStopHeight;
                zoomLevel = Math.min(Math.max(zoomLevel, 0), this.map.getNumZoomLevels() - 1);
            } else {
                zoomLevel += this.deltaY / this.zoomStopHeight;
                zoomLevel = Math.max(Math.round(zoomLevel), 0);
            }
            this.map.zoomTo(zoomLevel);
            this.mouseDragStart = null;
            this.zoomStart = null;
            this.deltaY = 0;
            OpenLayers.Event.stop(evt);
        }
    },
    moveZoomBar: function () {
        var newTop = ((this.map.getNumZoomLevels() - 1) - this.map.getZoom()) * this.zoomStopHeight + this.startTop + 1;
        this.slider.style.top = newTop + "px";
    },
    CLASS_NAME: "OpenLayers.Control.PanZoomBar"
});
OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, {
    isBaseLayer: true,
    url: null,
    extent: null,
    size: null,
    tile: null,
    aspectRatio: null,
    initialize: function (name, url, extent, size, options) {
        this.url = url;
        this.extent = extent;
        this.maxExtent = extent;
        this.size = size;
        OpenLayers.Layer.prototype.initialize.apply(this, [name, options]);
        this.aspectRatio = (this.extent.getHeight() / this.size.h) / (this.extent.getWidth() / this.size.w);
    },
    destroy: function () {
        if (this.tile) {
            this.removeTileMonitoringHooks(this.tile);
            this.tile.destroy();
            this.tile = null;
        }
        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.Image(this.name, this.url, this.extent, this.size, this.getOptions());
        }
        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
        return obj;
    },
    setMap: function (map) {
        if (this.options.maxResolution == null) {
            this.options.maxResolution = this.aspectRatio * this.extent.getWidth() / this.size.w;
        }
        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
        var firstRendering = (this.tile == null);
        if (zoomChanged || firstRendering) {
            this.setTileSize();
            var ul = new OpenLayers.LonLat(this.extent.left, this.extent.top);
            var ulPx = this.map.getLayerPxFromLonLat(ul);
            if (firstRendering) {
                this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize);
                this.addTileMonitoringHooks(this.tile);
            } else {
                this.tile.size = this.tileSize.clone();
                this.tile.position = ulPx.clone();
            }
            this.tile.draw();
        }
    },
    setTileSize: function () {
        var tileWidth = this.extent.getWidth() / this.map.getResolution();
        var tileHeight = this.extent.getHeight() / this.map.getResolution();
        this.tileSize = new OpenLayers.Size(tileWidth, tileHeight);
    },
    addTileMonitoringHooks: function (tile) {
        tile.onLoadStart = function () {
            this.events.triggerEvent("loadstart");
        };
        tile.events.register("loadstart", this, tile.onLoadStart);
        tile.onLoadEnd = function () {
            this.events.triggerEvent("loadend");
        };
        tile.events.register("loadend", this, tile.onLoadEnd);
        tile.events.register("unload", this, tile.onLoadEnd);
    },
    removeTileMonitoringHooks: function (tile) {
        tile.unload();
        tile.events.un({
            "loadstart": tile.onLoadStart,
            "loadend": tile.onLoadEnd,
            "unload": tile.onLoadEnd,
            scope: this
        });
    },
    setUrl: function (newUrl) {
        this.url = newUrl;
        this.tile.draw();
    },
    getURL: function (bounds) {
        return this.url;
    },
    CLASS_NAME: "OpenLayers.Layer.Image"
});
OpenLayers.Strategy = OpenLayers.Class({
    layer: null,
    options: null,
    active: null,
    autoActivate: true,
    autoDestroy: true,
    initialize: function (options) {
        OpenLayers.Util.extend(this, options);
        this.options = options;
        this.active = false;
    },
    destroy: function () {
        this.deactivate();
        this.layer = null;
        this.options = null;
    },
    setLayer: function (layer) {
        this.layer = layer;
    },
    activate: function () {
        if (!this.active) {
            this.active = true;
            return true;
        }
        return false;
    },
    deactivate: function () {
        if (this.active) {
            this.active = false;
            return true;
        }
        return false;
    },
    CLASS_NAME: "OpenLayers.Strategy"
});
OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, {
    EVENT_TYPES: ["start", "success", "fail"],
    events: null,
    auto: false,
    timer: null,
    initialize: function (options) {
        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
        this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES);
    },
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.call(this);
        if (activated) {
            if (this.auto) {
                if (typeof this.auto === "number") {
                    this.timer = window.setInterval(OpenLayers.Function.bind(this.save, this), this.auto * 1000);
                } else {
                    this.layer.events.on({
                        "featureadded": this.triggerSave,
                        "afterfeaturemodified": this.triggerSave,
                        scope: this
                    });
                }
            }
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            if (this.auto) {
                if (typeof this.auto === "number") {
                    window.clearInterval(this.timer);
                } else {
                    this.layer.events.un({
                        "featureadded": this.triggerSave,
                        "afterfeaturemodified": this.triggerSave,
                        scope: this
                    });
                }
            }
        }
        return deactivated;
    },
    triggerSave: function (event) {
        var feature = event.feature;
        if (feature.state === OpenLayers.State.INSERT || feature.state === OpenLayers.State.UPDATE || feature.state === OpenLayers.State.DELETE) {
            this.save([event.feature]);
        }
    },
    save: function (features) {
        if (!features) {
            features = this.layer.features;
        }
        this.events.triggerEvent("start", {
            features: features
        });
        var remote = this.layer.projection;
        var local = this.layer.map.getProjectionObject();
        if (!local.equals(remote)) {
            var len = features.length;
            var clones = new Array(len);
            var orig, clone;
            for (var i = 0; i < len; ++i) {
                orig = features[i];
                clone = orig.clone();
                clone.fid = orig.fid;
                clone.state = orig.state;
                if (orig.url) {
                    clone.url = orig.url;
                }
                clone._original = orig;
                clone.geometry.transform(local, remote);
                clones[i] = clone;
            }
            features = clones;
        }
        this.layer.protocol.commit(features, {
            callback: this.onCommit,
            scope: this
        });
    },
    onCommit: function (response) {
        var evt = {
            "response": response
        };
        if (response.success()) {
            var features = response.reqFeatures;
            var state, feature;
            var destroys = [];
            var insertIds = response.insertIds || [];
            var j = 0;
            for (var i = 0, len = features.length; i < len; ++i) {
                feature = features[i];
                feature = feature._original || feature;
                state = feature.state;
                if (state) {
                    if (state == OpenLayers.State.DELETE) {
                        destroys.push(feature);
                    } else if (state == OpenLayers.State.INSERT) {
                        feature.fid = insertIds[j];
                        ++j;
                    }
                    feature.state = null;
                }
            }
            if (destroys.length > 0) {
                this.layer.destroyFeatures(destroys);
            }
            this.events.triggerEvent("success", evt);
        } else {
            this.events.triggerEvent("fail", evt);
        }
    },
    CLASS_NAME: "OpenLayers.Strategy.Save"
});
OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, {
    extractWaypoints: true,
    extractTracks: true,
    extractRoutes: true,
    extractAttributes: true,
    initialize: function (options) {
        this.externalProjection = new OpenLayers.Projection("EPSG:4326");
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (doc) {
        if (typeof doc == "string") {
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
        }
        var features = [];
        if (this.extractTracks) {
            var tracks = doc.getElementsByTagName("trk");
            for (var i = 0, len = tracks.length; i < len; i++) {
                var attrs = {};
                if (this.extractAttributes) {
                    attrs = this.parseAttributes(tracks[i]);
                }
                var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg");
                for (var j = 0, seglen = segs.length; j < seglen; j++) {
                    var track = this.extractSegment(segs[j], "trkpt");
                    features.push(new OpenLayers.Feature.Vector(track, attrs));
                }
            }
        }
        if (this.extractRoutes) {
            var routes = doc.getElementsByTagName("rte");
            for (var k = 0, klen = routes.length; k < klen; k++) {
                var attrs = {};
                if (this.extractAttributes) {
                    attrs = this.parseAttributes(routes[k]);
                }
                var route = this.extractSegment(routes[k], "rtept");
                features.push(new OpenLayers.Feature.Vector(route, attrs));
            }
        }
        if (this.extractWaypoints) {
            var waypoints = doc.getElementsByTagName("wpt");
            for (var l = 0, len = waypoints.length; l < len; l++) {
                var attrs = {};
                if (this.extractAttributes) {
                    attrs = this.parseAttributes(waypoints[l]);
                }
                var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat"));
                features.push(new OpenLayers.Feature.Vector(wpt, attrs));
            }
        }
        if (this.internalProjection && this.externalProjection) {
            for (var g = 0, featLength = features.length; g < featLength; g++) {
                features[g].geometry.transform(this.externalProjection, this.internalProjection);
            }
        }
        return features;
    },
    extractSegment: function (segment, segmentType) {
        var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType);
        var point_features = [];
        for (var i = 0, len = points.length; i < len; i++) {
            point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat")));
        }
        return new OpenLayers.Geometry.LineString(point_features);
    },
    parseAttributes: function (node) {
        var attributes = {};
        var attrNode = node.firstChild,
            value, name;
        while (attrNode) {
            if (attrNode.nodeType == 1) {
                value = attrNode.firstChild;
                if (value.nodeType == 3 || value.nodeType == 4) {
                    name = (attrNode.prefix) ? attrNode.nodeName.split(":")[1] : attrNode.nodeName;
                    if (name != "trkseg" && name != "rtept") {
                        attributes[name] = value.nodeValue;
                    }
                }
            }
            attrNode = attrNode.nextSibling;
        }
        return attributes;
    },
    CLASS_NAME: "OpenLayers.Format.GPX"
});
OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.1.1",
    getVersion: function (root, options) {
        var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this, arguments);
        if (version == "1.1.1" || version == "1.1.0") {
            version = "1.1";
        }
        return version;
    },
    CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer"
});
OpenLayers.Format.WMSDescribeLayer.v1_1 = OpenLayers.Class(OpenLayers.Format.WMSDescribeLayer, {
    initialize: function (options) {
        OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var root = data.documentElement;
        var children = root.childNodes;
        var describelayer = [];
        var childNode, nodeName;
        for (var i = 0; i < children.length; ++i) {
            childNode = children[i];
            nodeName = childNode.nodeName;
            if (nodeName == 'LayerDescription') {
                var layerName = childNode.getAttribute('name');
                var owsType = '';
                var owsURL = '';
                var typeName = '';
                if (childNode.getAttribute('owsType')) {
                    owsType = childNode.getAttribute('owsType');
                    owsURL = childNode.getAttribute('owsURL');
                } else {
                    if (childNode.getAttribute('wfs') != '') {
                        owsType = 'WFS';
                        owsURL = childNode.getAttribute('wfs');
                    } else if (childNode.getAttribute('wcs') != '') {
                        owsType = 'WCS';
                        owsURL = childNode.getAttribute('wcs');
                    }
                }
                var query = childNode.getElementsByTagName('Query');
                if (query.length > 0) {
                    typeName = query[0].getAttribute('typeName');
                    if (!typeName) {
                        typeName = query[0].getAttribute('typename');
                    }
                }
                describelayer.push({
                    layerName: layerName,
                    owsType: owsType,
                    owsURL: owsURL,
                    typeName: typeName
                });
            }
        }
        return describelayer;
    },
    CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer.v1_1"
});
OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.1.0",
    stringifyOutput: true,
    CLASS_NAME: "OpenLayers.Format.XLS"
});
OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        xls: "http://www.opengis.net/xls",
        gml: "http://www.opengis.net/gml",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    xy: true,
    defaultPrefix: "xls",
    schemaLocation: null,
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var xls = {};
        this.readChildNodes(data, xls);
        return xls;
    },
    readers: {
        "xls": {
            "XLS": function (node, xls) {
                xls.version = node.getAttribute("version");
                this.readChildNodes(node, xls);
            },
            "Response": function (node, xls) {
                this.readChildNodes(node, xls);
            },
            "GeocodeResponse": function (node, xls) {
                xls.responseLists = [];
                this.readChildNodes(node, xls);
            },
            "GeocodeResponseList": function (node, xls) {
                var responseList = {
                    features: [],
                    numberOfGeocodedAddresses: parseInt(node.getAttribute("numberOfGeocodedAddresses"))
                };
                xls.responseLists.push(responseList);
                this.readChildNodes(node, responseList);
            },
            "GeocodedAddress": function (node, responseList) {
                var feature = new OpenLayers.Feature.Vector();
                responseList.features.push(feature);
                this.readChildNodes(node, feature);
                feature.geometry = feature.components[0];
            },
            "GeocodeMatchCode": function (node, feature) {
                feature.attributes.matchCode = {
                    accuracy: parseFloat(node.getAttribute("accuracy")),
                    matchType: node.getAttribute("matchType")
                };
            },
            "Address": function (node, feature) {
                var address = {
                    countryCode: node.getAttribute("countryCode"),
                    addressee: node.getAttribute("addressee"),
                    street: [],
                    place: []
                };
                feature.attributes.address = address;
                this.readChildNodes(node, address);
            },
            "freeFormAddress": function (node, address) {
                address.freeFormAddress = this.getChildValue(node);
            },
            "StreetAddress": function (node, address) {
                this.readChildNodes(node, address);
            },
            "Building": function (node, address) {
                address.building = {
                    'number': node.getAttribute("number"),
                    subdivision: node.getAttribute("subdivision"),
                    buildingName: node.getAttribute("buildingName")
                };
            },
            "Street": function (node, address) {
                address.street.push(this.getChildValue(node));
            },
            "Place": function (node, address) {
                address.place[node.getAttribute("type")] = this.getChildValue(node);
            },
            "PostalCode": function (node, address) {
                address.postalCode = this.getChildValue(node);
            }
        },
        "gml": OpenLayers.Format.GML.v3.prototype.readers.gml
    },
    write: function (request) {
        return this.writers.xls.XLS.apply(this, [request]);
    },
    writers: {
        "xls": {
            "XLS": function (request) {
                var root = this.createElementNSPlus("xls:XLS", {
                    attributes: {
                        "version": this.VERSION,
                        "xsi:schemaLocation": this.schemaLocation
                    }
                });
                this.writeNode("RequestHeader", request.header, root);
                this.writeNode("Request", request, root);
                return root;
            },
            "RequestHeader": function (header) {
                return this.createElementNSPlus("xls:RequestHeader");
            },
            "Request": function (request) {
                var node = this.createElementNSPlus("xls:Request", {
                    attributes: {
                        methodName: "GeocodeRequest",
                        requestID: request.requestID || "",
                        version: this.VERSION
                    }
                });
                this.writeNode("GeocodeRequest", request.addresses, node);
                return node;
            },
            "GeocodeRequest": function (addresses) {
                var node = this.createElementNSPlus("xls:GeocodeRequest");
                for (var i = 0, len = addresses.length; i < len; i++) {
                    this.writeNode("Address", addresses[i], node);
                }
                return node;
            },
            "Address": function (address) {
                var node = this.createElementNSPlus("xls:Address", {
                    attributes: {
                        countryCode: address.countryCode
                    }
                });
                if (address.freeFormAddress) {
                    this.writeNode("freeFormAddess", address.freeFormAddress, node);
                } else {
                    if (address.street) {
                        this.writeNode("StreetAddress", address, node);
                    }
                    if (address.municipality) {
                        this.writeNode("Municipality", address.municipality, node);
                    }
                    if (address.countrySubdivision) {
                        this.writeNode("CountrySubdivision", address.countrySubdivision, node);
                    }
                    if (address.postalCode) {
                        this.writeNode("PostalCode", address.postalCode, node);
                    }
                }
                return node;
            },
            "freeFormAddress": function (freeFormAddress) {
                return this.createElementNSPlus("freeFormAddress", {
                    value: freeFormAddress
                });
            },
            "StreetAddress": function (address) {
                var node = this.createElementNSPlus("xls:StreetAddress");
                if (address.building) {
                    this.writeNode(node, "Building", address.building);
                }
                var street = address.street;
                if (!(OpenLayers.Util.isArray(street))) {
                    street = [street];
                }
                for (var i = 0, len = street.length; i < len; i++) {
                    this.writeNode("Street", street[i], node);
                }
                return node;
            },
            "Building": function (building) {
                return this.createElementNSPlus("xls:Building", {
                    attributes: {
                        "number": building["number"],
                        "subdivision": building.subdivision,
                        "buildingName": building.buildingName
                    }
                });
            },
            "Street": function (street) {
                return this.createElementNSPlus("xls:Street", {
                    value: street
                });
            },
            "Municipality": function (municipality) {
                return this.createElementNSPlus("xls:Place", {
                    attributes: {
                        type: "Municipality"
                    },
                    value: municipality
                });
            },
            "CountrySubdivision": function (countrySubdivision) {
                return this.createElementNSPlus("xls:Place", {
                    attributes: {
                        type: "CountrySubdivision"
                    },
                    value: countrySubdivision
                });
            },
            "PostalCode": function (postalCode) {
                return this.createElementNSPlus("xls:PostalCode", {
                    value: postalCode
                });
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.XLS.v1"
});
OpenLayers.Renderer = OpenLayers.Class({
    container: null,
    root: null,
    extent: null,
    locked: false,
    size: null,
    resolution: null,
    map: null,
    initialize: function (containerID, options) {
        this.container = OpenLayers.Util.getElement(containerID);
        OpenLayers.Util.extend(this, options);
    },
    destroy: function () {
        this.container = null;
        this.extent = null;
        this.size = null;
        this.resolution = null;
        this.map = null;
    },
    supported: function () {
        return false;
    },
    setExtent: function (extent, resolutionChanged) {
        this.extent = extent.clone();
        if (resolutionChanged) {
            this.resolution = null;
        }
    },
    setSize: function (size) {
        this.size = size.clone();
        this.resolution = null;
    },
    getResolution: function () {
        this.resolution = this.resolution || this.map.getResolution();
        return this.resolution;
    },
    drawFeature: function (feature, style) {
        if (style == null) {
            style = feature.style;
        }
        if (feature.geometry) {
            var bounds = feature.geometry.getBounds();
            if (bounds) {
                if (!bounds.intersectsBounds(this.extent)) {
                    style = {
                        display: "none"
                    };
                }
                var rendered = this.drawGeometry(feature.geometry, style, feature.id);
                if (style.display != "none" && style.label && rendered !== false) {
                    var location = feature.geometry.getCentroid();
                    if (style.labelXOffset || style.labelYOffset) {
                        var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
                        var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
                        var res = this.getResolution();
                        location.move(xOffset * res, yOffset * res);
                    }
                    this.drawText(feature.id, style, location);
                } else {
                    this.removeText(feature.id);
                }
                return rendered;
            }
        }
    },
    drawGeometry: function (geometry, style, featureId) {},
    drawText: function (featureId, style, location) {},
    removeText: function (featureId) {},
    clear: function () {},
    getFeatureIdFromEvent: function (evt) {},
    eraseFeatures: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        for (var i = 0, len = features.length; i < len; ++i) {
            var feature = features[i];
            this.eraseGeometry(feature.geometry, feature.id);
            this.removeText(feature.id);
        }
    },
    eraseGeometry: function (geometry, featureId) {},
    moveRoot: function (renderer) {},
    getRenderLayerId: function () {
        return this.container.id;
    },
    applyDefaultSymbolizer: function (symbolizer) {
        var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer);
        if (symbolizer.stroke === false) {
            delete result.strokeWidth;
            delete result.strokeColor;
        }
        if (symbolizer.fill === false) {
            delete result.fillColor;
        }
        OpenLayers.Util.extend(result, symbolizer);
        return result;
    },
    CLASS_NAME: "OpenLayers.Renderer"
});
OpenLayers.Renderer.defaultSymbolizer = {
    fillColor: "#000000",
    strokeColor: "#000000",
    strokeWidth: 2,
    fillOpacity: 1,
    strokeOpacity: 1,
    pointRadius: 0
};
OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, {
    hitDetection: true,
    hitOverflow: 0,
    canvas: null,
    features: null,
    pendingRedraw: false,
    initialize: function (containerID, options) {
        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
        this.root = document.createElement("canvas");
        this.container.appendChild(this.root);
        this.canvas = this.root.getContext("2d");
        this.features = {};
        if (this.hitDetection) {
            this.hitCanvas = document.createElement("canvas");
            this.hitContext = this.hitCanvas.getContext("2d");
        }
    },
    eraseGeometry: function (geometry, featureId) {
        this.eraseFeatures(this.features[featureId][0]);
    },
    supported: function () {
        var canvas = document.createElement("canvas");
        return !!canvas.getContext;
    },
    setSize: function (size) {
        this.size = size.clone();
        var root = this.root;
        root.style.width = size.w + "px";
        root.style.height = size.h + "px";
        root.width = size.w;
        root.height = size.h;
        this.resolution = null;
        if (this.hitDetection) {
            var hitCanvas = this.hitCanvas;
            hitCanvas.style.width = size.w + "px";
            hitCanvas.style.height = size.h + "px";
            hitCanvas.width = size.w;
            hitCanvas.height = size.h;
        }
    },
    drawFeature: function (feature, style) {
        var rendered;
        if (feature.geometry) {
            style = this.applyDefaultSymbolizer(style || feature.style);
            var bounds = feature.geometry.getBounds();
            rendered = (style.display !== "none") && !! bounds && bounds.intersectsBounds(this.extent);
            if (rendered) {
                this.features[feature.id] = [feature, style];
            } else {
                delete(this.features[feature.id]);
            }
            this.pendingRedraw = true;
        }
        if (this.pendingRedraw && !this.locked) {
            this.redraw();
            this.pendingRedraw = false;
        }
        return rendered;
    },
    drawGeometry: function (geometry, style, featureId) {
        var className = geometry.CLASS_NAME;
        if ((className == "OpenLayers.Geometry.Collection") || (className == "OpenLayers.Geometry.MultiPoint") || (className == "OpenLayers.Geometry.MultiLineString") || (className == "OpenLayers.Geometry.MultiPolygon")) {
            for (var i = 0; i < geometry.components.length; i++) {
                this.drawGeometry(geometry.components[i], style, featureId);
            }
            return;
        }
        switch (geometry.CLASS_NAME) {
        case "OpenLayers.Geometry.Point":
            this.drawPoint(geometry, style, featureId);
            break;
        case "OpenLayers.Geometry.LineString":
            this.drawLineString(geometry, style, featureId);
            break;
        case "OpenLayers.Geometry.LinearRing":
            this.drawLinearRing(geometry, style, featureId);
            break;
        case "OpenLayers.Geometry.Polygon":
            this.drawPolygon(geometry, style, featureId);
            break;
        default:
            break;
        }
    },
    drawExternalGraphic: function (geometry, style, featureId) {
        var img = new Image();
        if (style.graphicTitle) {
            img.title = style.graphicTitle;
        }
        var width = style.graphicWidth || style.graphicHeight;
        var height = style.graphicHeight || style.graphicWidth;
        width = width ? width : style.pointRadius * 2;
        height = height ? height : style.pointRadius * 2;
        var xOffset = (style.graphicXOffset != undefined) ? style.graphicXOffset : -(0.5 * width);
        var yOffset = (style.graphicYOffset != undefined) ? style.graphicYOffset : -(0.5 * height);
        var opacity = style.graphicOpacity || style.fillOpacity;
        var onLoad = function () {
                if (!this.features[featureId]) {
                    return;
                }
                var pt = this.getLocalXY(geometry);
                var p0 = pt[0];
                var p1 = pt[1];
                if (!isNaN(p0) && !isNaN(p1)) {
                    var x = (p0 + xOffset) | 0;
                    var y = (p1 + yOffset) | 0;
                    var canvas = this.canvas;
                    canvas.globalAlpha = opacity;
                    var factor = OpenLayers.Renderer.Canvas.drawImageScaleFactor || (OpenLayers.Renderer.Canvas.drawImageScaleFactor = /android 2.1/.test(navigator.userAgent.toLowerCase()) ? 320 / window.screen.width : 1);
                    canvas.drawImage(img, x * factor, y * factor, width * factor, height * factor);
                    if (this.hitDetection) {
                        this.setHitContextStyle("fill", featureId);
                        this.hitContext.fillRect(x, y, width, height);
                    }
                }
            };
        img.onload = OpenLayers.Function.bind(onLoad, this);
        img.src = style.externalGraphic;
    },
    setCanvasStyle: function (type, style) {
        if (type === "fill") {
            this.canvas.globalAlpha = style['fillOpacity'];
            this.canvas.fillStyle = style['fillColor'];
        } else if (type === "stroke") {
            this.canvas.globalAlpha = style['strokeOpacity'];
            this.canvas.strokeStyle = style['strokeColor'];
            this.canvas.lineWidth = style['strokeWidth'];
        } else {
            this.canvas.globalAlpha = 0;
            this.canvas.lineWidth = 1;
        }
    },
    featureIdToHex: function (featureId) {
        var id = Number(featureId.split("_").pop()) + 1;
        if (id >= 16777216) {
            this.hitOverflow = id - 16777215;
            id = id % 16777216 + 1;
        }
        var hex = "000000" + id.toString(16);
        var len = hex.length;
        hex = "#" + hex.substring(len - 6, len);
        return hex;
    },
    setHitContextStyle: function (type, featureId, symbolizer) {
        var hex = this.featureIdToHex(featureId);
        if (type == "fill") {
            this.hitContext.globalAlpha = 1.0;
            this.hitContext.fillStyle = hex;
        } else if (type == "stroke") {
            this.hitContext.globalAlpha = 1.0;
            this.hitContext.strokeStyle = hex;
            this.hitContext.lineWidth = symbolizer.strokeWidth + 2;
        } else {
            this.hitContext.globalAlpha = 0;
            this.hitContext.lineWidth = 1;
        }
    },
    drawPoint: function (geometry, style, featureId) {
        if (style.graphic !== false) {
            if (style.externalGraphic) {
                this.drawExternalGraphic(geometry, style, featureId);
            } else {
                var pt = this.getLocalXY(geometry);
                var p0 = pt[0];
                var p1 = pt[1];
                if (!isNaN(p0) && !isNaN(p1)) {
                    var twoPi = Math.PI * 2;
                    var radius = style.pointRadius;
                    if (style.fill !== false) {
                        this.setCanvasStyle("fill", style);
                        this.canvas.beginPath();
                        this.canvas.arc(p0, p1, radius, 0, twoPi, true);
                        this.canvas.fill();
                        if (this.hitDetection) {
                            this.setHitContextStyle("fill", featureId, style);
                            this.hitContext.beginPath();
                            this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
                            this.hitContext.fill();
                        }
                    }
                    if (style.stroke !== false) {
                        this.setCanvasStyle("stroke", style);
                        this.canvas.beginPath();
                        this.canvas.arc(p0, p1, radius, 0, twoPi, true);
                        this.canvas.stroke();
                        if (this.hitDetection) {
                            this.setHitContextStyle("stroke", featureId, style);
                            this.hitContext.beginPath();
                            this.hitContext.arc(p0, p1, radius, 0, twoPi, true);
                            this.hitContext.stroke();
                        }
                        this.setCanvasStyle("reset");
                    }
                }
            }
        }
    },
    drawLineString: function (geometry, style, featureId) {
        style = OpenLayers.Util.applyDefaults({
            fill: false
        }, style);
        this.drawLinearRing(geometry, style, featureId);
    },
    drawLinearRing: function (geometry, style, featureId) {
        if (style.fill !== false) {
            this.setCanvasStyle("fill", style);
            this.renderPath(this.canvas, geometry, style, featureId, "fill");
            if (this.hitDetection) {
                this.setHitContextStyle("fill", featureId, style);
                this.renderPath(this.hitContext, geometry, style, featureId, "fill");
            }
        }
        if (style.stroke !== false) {
            this.setCanvasStyle("stroke", style);
            this.renderPath(this.canvas, geometry, style, featureId, "stroke");
            if (this.hitDetection) {
                this.setHitContextStyle("stroke", featureId, style);
                this.renderPath(this.hitContext, geometry, style, featureId, "stroke");
            }
        }
        this.setCanvasStyle("reset");
    },
    renderPath: function (context, geometry, style, featureId, type) {
        var components = geometry.components;
        var len = components.length;
        context.beginPath();
        var start = this.getLocalXY(components[0]);
        var x = start[0];
        var y = start[1];
        if (!isNaN(x) && !isNaN(y)) {
            context.moveTo(start[0], start[1]);
            for (var i = 1; i < len; ++i) {
                var pt = this.getLocalXY(components[i]);
                context.lineTo(pt[0], pt[1]);
            }
            if (type === "fill") {
                context.fill();
            } else {
                context.stroke();
            }
        }
    },
    drawPolygon: function (geometry, style, featureId) {
        var components = geometry.components;
        var len = components.length;
        this.drawLinearRing(components[0], style, featureId);
        for (var i = 1; i < len; ++i) {
            this.canvas.globalCompositeOperation = "destination-out";
            if (this.hitDetection) {
                this.hitContext.globalCompositeOperation = "destination-out";
            }
            this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({
                stroke: false,
                fillOpacity: 1.0
            }, style), featureId);
            this.canvas.globalCompositeOperation = "source-over";
            if (this.hitDetection) {
                this.hitContext.globalCompositeOperation = "source-over";
            }
            this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({
                fill: false
            }, style), featureId);
        }
    },
    drawText: function (location, style) {
        style = OpenLayers.Util.extend({
            fontColor: "#000000",
            labelAlign: "cm"
        }, style);
        var pt = this.getLocalXY(location);
        this.setCanvasStyle("reset");
        this.canvas.fillStyle = style.fontColor;
        this.canvas.globalAlpha = style.fontOpacity || 1.0;
        var fontStyle = [style.fontStyle ? style.fontStyle : "normal", "normal", style.fontWeight ? style.fontWeight : "normal", style.fontSize ? style.fontSize : "1em", style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
        var labelRows = style.label.split('\n');
        var numRows = labelRows.length;
        if (this.canvas.fillText) {
            this.canvas.font = fontStyle;
            this.canvas.textAlign = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || "center";
            this.canvas.textBaseline = OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || "middle";
            var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
            if (vfactor == null) {
                vfactor = -.5;
            }
            var lineHeight = this.canvas.measureText('Mg').height || this.canvas.measureText('xx').width;
            pt[1] += lineHeight * vfactor * (numRows - 1);
            for (var i = 0; i < numRows; i++) {
                this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i));
            }
        } else if (this.canvas.mozDrawText) {
            this.canvas.mozTextStyle = fontStyle;
            var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
            if (hfactor == null) {
                hfactor = -.5;
            }
            var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
            if (vfactor == null) {
                vfactor = -.5;
            }
            var lineHeight = this.canvas.mozMeasureText('xx');
            pt[1] += lineHeight * (1 + (vfactor * numRows));
            for (var i = 0; i < numRows; i++) {
                var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i]));
                var y = pt[1] + (i * lineHeight);
                this.canvas.translate(x, y);
                this.canvas.mozDrawText(labelRows[i]);
                this.canvas.translate(-x, -y);
            }
        }
        this.setCanvasStyle("reset");
    },
    getLocalXY: function (point) {
        var resolution = this.getResolution();
        var extent = this.extent;
        var x = (point.x / resolution + (-extent.left / resolution));
        var y = ((extent.top / resolution) - point.y / resolution);
        return [x, y];
    },
    clear: function () {
        var height = this.root.height;
        var width = this.root.width;
        this.canvas.clearRect(0, 0, width, height);
        this.features = {};
        if (this.hitDetection) {
            this.hitContext.clearRect(0, 0, width, height);
        }
    },
    getFeatureIdFromEvent: function (evt) {
        var feature = null;
        if (this.hitDetection) {
            if (!this.map.dragging) {
                var xy = evt.xy;
                var x = xy.x | 0;
                var y = xy.y | 0;
                var data = this.hitContext.getImageData(x, y, 1, 1).data;
                if (data[3] === 255) {
                    var id = data[2] + (256 * (data[1] + (256 * data[0])));
                    if (id) {
                        feature = this.features["OpenLayers.Feature.Vector_" + (id - 1 + this.hitOverflow)][0];
                    }
                }
            }
        }
        return feature;
    },
    eraseFeatures: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        for (var i = 0; i < features.length; ++i) {
            delete this.features[features[i].id];
        }
        this.redraw();
    },
    redraw: function () {
        if (!this.locked) {
            var height = this.root.height;
            var width = this.root.width;
            this.canvas.clearRect(0, 0, width, height);
            if (this.hitDetection) {
                this.hitContext.clearRect(0, 0, width, height);
            }
            var labelMap = [];
            var feature, style;
            for (var id in this.features) {
                if (!this.features.hasOwnProperty(id)) {
                    continue;
                }
                feature = this.features[id][0];
                style = this.features[id][1];
                this.drawGeometry(feature.geometry, style, feature.id);
                if (style.label) {
                    labelMap.push([feature, style]);
                }
            }
            var item;
            for (var i = 0, len = labelMap.length; i < len; ++i) {
                item = labelMap[i];
                this.drawText(item[0].geometry.getCentroid(), item[1]);
            }
        }
    },
    CLASS_NAME: "OpenLayers.Renderer.Canvas"
});
OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
    "l": "left",
    "r": "right",
    "t": "top",
    "b": "bottom"
};
OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
    "l": 0,
    "r": -1,
    "t": 0,
    "b": -1
};
OpenLayers.Renderer.Canvas.drawImageScaleFactor = null;
OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, {
    checkTags: false,
    interestingTagsExclude: null,
    areaTags: null,
    initialize: function (options) {
        var layer_defaults = {
            'interestingTagsExclude': ['source', 'source_ref', 'source:ref', 'history', 'attribution', 'created_by'],
            'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins', 'historic', 'landuse', 'military', 'natural', 'sport']
        };
        layer_defaults = OpenLayers.Util.extend(layer_defaults, options);
        var interesting = {};
        for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) {
            interesting[layer_defaults.interestingTagsExclude[i]] = true;
        }
        layer_defaults.interestingTagsExclude = interesting;
        var area = {};
        for (var i = 0; i < layer_defaults.areaTags.length; i++) {
            area[layer_defaults.areaTags[i]] = true;
        }
        layer_defaults.areaTags = area;
        this.externalProjection = new OpenLayers.Projection("EPSG:4326");
        OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]);
    },
    read: function (doc) {
        if (typeof doc == "string") {
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
        }
        var nodes = this.getNodes(doc);
        var ways = this.getWays(doc);
        var feat_list = new Array(ways.length);
        for (var i = 0; i < ways.length; i++) {
            var point_list = new Array(ways[i].nodes.length);
            var poly = this.isWayArea(ways[i]) ? 1 : 0;
            for (var j = 0; j < ways[i].nodes.length; j++) {
                var node = nodes[ways[i].nodes[j]];
                var point = new OpenLayers.Geometry.Point(node.lon, node.lat);
                point.osm_id = parseInt(ways[i].nodes[j]);
                point_list[j] = point;
                node.used = true;
            }
            var geometry = null;
            if (poly) {
                geometry = new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(point_list));
            } else {
                geometry = new OpenLayers.Geometry.LineString(point_list);
            }
            if (this.internalProjection && this.externalProjection) {
                geometry.transform(this.externalProjection, this.internalProjection);
            }
            var feat = new OpenLayers.Feature.Vector(geometry, ways[i].tags);
            feat.osm_id = parseInt(ways[i].id);
            feat.fid = "way." + feat.osm_id;
            feat_list[i] = feat;
        }
        for (var node_id in nodes) {
            var node = nodes[node_id];
            if (!node.used || this.checkTags) {
                var tags = null;
                if (this.checkTags) {
                    var result = this.getTags(node.node, true);
                    if (node.used && !result[1]) {
                        continue;
                    }
                    tags = result[0];
                } else {
                    tags = this.getTags(node.node);
                }
                var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(node['lon'], node['lat']), tags);
                if (this.internalProjection && this.externalProjection) {
                    feat.geometry.transform(this.externalProjection, this.internalProjection);
                }
                feat.osm_id = parseInt(node_id);
                feat.fid = "node." + feat.osm_id;
                feat_list.push(feat);
            }
            node.node = null;
        }
        return feat_list;
    },
    getNodes: function (doc) {
        var node_list = doc.getElementsByTagName("node");
        var nodes = {};
        for (var i = 0; i < node_list.length; i++) {
            var node = node_list[i];
            var id = node.getAttribute("id");
            nodes[id] = {
                'lat': node.getAttribute("lat"),
                'lon': node.getAttribute("lon"),
                'node': node
            };
        }
        return nodes;
    },
    getWays: function (doc) {
        var way_list = doc.getElementsByTagName("way");
        var return_ways = [];
        for (var i = 0; i < way_list.length; i++) {
            var way = way_list[i];
            var way_object = {
                id: way.getAttribute("id")
            };
            way_object.tags = this.getTags(way);
            var node_list = way.getElementsByTagName("nd");
            way_object.nodes = new Array(node_list.length);
            for (var j = 0; j < node_list.length; j++) {
                way_object.nodes[j] = node_list[j].getAttribute("ref");
            }
            return_ways.push(way_object);
        }
        return return_ways;
    },
    getTags: function (dom_node, interesting_tags) {
        var tag_list = dom_node.getElementsByTagName("tag");
        var tags = {};
        var interesting = false;
        for (var j = 0; j < tag_list.length; j++) {
            var key = tag_list[j].getAttribute("k");
            tags[key] = tag_list[j].getAttribute("v");
            if (interesting_tags) {
                if (!this.interestingTagsExclude[key]) {
                    interesting = true;
                }
            }
        }
        return interesting_tags ? [tags, interesting] : tags;
    },
    isWayArea: function (way) {
        var poly_shaped = false;
        var poly_tags = false;
        if (way.nodes[0] == way.nodes[way.nodes.length - 1]) {
            poly_shaped = true;
        }
        if (this.checkTags) {
            for (var key in way.tags) {
                if (this.areaTags[key]) {
                    poly_tags = true;
                    break;
                }
            }
        }
        return poly_shaped && (this.checkTags ? poly_tags : true);
    },
    write: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        this.osm_id = 1;
        this.created_nodes = {};
        var root_node = this.createElementNS(null, "osm");
        root_node.setAttribute("version", "0.5");
        root_node.setAttribute("generator", "OpenLayers " + OpenLayers.VERSION_NUMBER);
        for (var i = features.length - 1; i >= 0; i--) {
            var nodes = this.createFeatureNodes(features[i]);
            for (var j = 0; j < nodes.length; j++) {
                root_node.appendChild(nodes[j]);
            }
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]);
    },
    createFeatureNodes: function (feature) {
        var nodes = [];
        var className = feature.geometry.CLASS_NAME;
        var type = className.substring(className.lastIndexOf(".") + 1);
        type = type.toLowerCase();
        var builder = this.createXML[type];
        if (builder) {
            nodes = builder.apply(this, [feature]);
        }
        return nodes;
    },
    createXML: {
        'point': function (point) {
            var id = null;
            var geometry = point.geometry ? point.geometry : point;
            if (this.internalProjection && this.externalProjection) {
                geometry = geometry.clone();
                geometry.transform(this.internalProjection, this.externalProjection);
            }
            var already_exists = false;
            if (point.osm_id) {
                id = point.osm_id;
                if (this.created_nodes[id]) {
                    already_exists = true;
                }
            } else {
                id = -this.osm_id;
                this.osm_id++;
            }
            if (already_exists) {
                node = this.created_nodes[id];
            } else {
                var node = this.createElementNS(null, "node");
            }
            this.created_nodes[id] = node;
            node.setAttribute("id", id);
            node.setAttribute("lon", geometry.x);
            node.setAttribute("lat", geometry.y);
            if (point.attributes) {
                this.serializeTags(point, node);
            }
            this.setState(point, node);
            return already_exists ? [] : [node];
        },
        linestring: function (feature) {
            var id;
            var nodes = [];
            var geometry = feature.geometry;
            if (feature.osm_id) {
                id = feature.osm_id;
            } else {
                id = -this.osm_id;
                this.osm_id++;
            }
            var way = this.createElementNS(null, "way");
            way.setAttribute("id", id);
            for (var i = 0; i < geometry.components.length; i++) {
                var node = this.createXML['point'].apply(this, [geometry.components[i]]);
                if (node.length) {
                    node = node[0];
                    var node_ref = node.getAttribute("id");
                    nodes.push(node);
                } else {
                    node_ref = geometry.components[i].osm_id;
                    node = this.created_nodes[node_ref];
                }
                this.setState(feature, node);
                var nd_dom = this.createElementNS(null, "nd");
                nd_dom.setAttribute("ref", node_ref);
                way.appendChild(nd_dom);
            }
            this.serializeTags(feature, way);
            nodes.push(way);
            return nodes;
        },
        polygon: function (feature) {
            var attrs = OpenLayers.Util.extend({
                'area': 'yes'
            }, feature.attributes);
            var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs);
            feat.osm_id = feature.osm_id;
            return this.createXML['linestring'].apply(this, [feat]);
        }
    },
    serializeTags: function (feature, node) {
        for (var key in feature.attributes) {
            var tag = this.createElementNS(null, "tag");
            tag.setAttribute("k", key);
            tag.setAttribute("v", feature.attributes[key]);
            node.appendChild(tag);
        }
    },
    setState: function (feature, node) {
        if (feature.state) {
            var state = null;
            switch (feature.state) {
            case OpenLayers.State.UPDATE:
                state = "modify";
            case OpenLayers.State.DELETE:
                state = "delete";
            }
            if (state) {
                node.setAttribute("action", state);
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.OSM"
});
OpenLayers.Handler = OpenLayers.Class({
    id: null,
    control: null,
    map: null,
    keyMask: null,
    active: false,
    evt: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Util.extend(this, options);
        this.control = control;
        this.callbacks = callbacks;
        var map = this.map || control.map;
        if (map) {
            this.setMap(map);
        }
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    setMap: function (map) {
        this.map = map;
    },
    checkModifiers: function (evt) {
        if (this.keyMask == null) {
            return true;
        }
        var keyModifiers = (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0);
        return (keyModifiers == this.keyMask);
    },
    activate: function () {
        if (this.active) {
            return false;
        }
        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
        for (var i = 0, len = events.length; i < len; i++) {
            if (this[events[i]]) {
                this.register(events[i], this[events[i]]);
            }
        }
        this.active = true;
        return true;
    },
    deactivate: function () {
        if (!this.active) {
            return false;
        }
        var events = OpenLayers.Events.prototype.BROWSER_EVENTS;
        for (var i = 0, len = events.length; i < len; i++) {
            if (this[events[i]]) {
                this.unregister(events[i], this[events[i]]);
            }
        }
        this.active = false;
        return true;
    },
    callback: function (name, args) {
        if (name && this.callbacks[name]) {
            this.callbacks[name].apply(this.control, args);
        }
    },
    register: function (name, method) {
        this.map.events.registerPriority(name, this, method);
        this.map.events.registerPriority(name, this, this.setEvent);
    },
    unregister: function (name, method) {
        this.map.events.unregister(name, this, method);
        this.map.events.unregister(name, this, this.setEvent);
    },
    setEvent: function (evt) {
        this.evt = evt;
        return true;
    },
    destroy: function () {
        this.deactivate();
        this.control = this.map = null;
    },
    CLASS_NAME: "OpenLayers.Handler"
});
OpenLayers.Handler.MOD_NONE = 0;
OpenLayers.Handler.MOD_SHIFT = 1;
OpenLayers.Handler.MOD_CTRL = 2;
OpenLayers.Handler.MOD_ALT = 4;
OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, {
    started: false,
    stopDown: true,
    dragging: false,
    touch: false,
    last: null,
    start: null,
    lastMoveEvt: null,
    oldOnselectstart: null,
    interval: 0,
    timeoutId: null,
    documentDrag: false,
    documentEvents: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
        if (this.documentDrag === true) {
            var me = this;
            this._docMove = function (evt) {
                me.mousemove({
                    xy: {
                        x: evt.clientX,
                        y: evt.clientY
                    },
                    element: document
                });
            };
            this._docUp = function (evt) {
                me.mouseup({
                    xy: {
                        x: evt.clientX,
                        y: evt.clientY
                    }
                });
            };
        }
    },
    dragstart: function (evt) {
        var propagate = true;
        this.dragging = false;
        if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) {
            this.started = true;
            this.start = evt.xy;
            this.last = evt.xy;
            OpenLayers.Element.addClass(this.map.viewPortDiv, "olDragDown");
            this.down(evt);
            this.callback("down", [evt.xy]);
            OpenLayers.Event.stop(evt);
            if (!this.oldOnselectstart) {
                this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True;
            }
            document.onselectstart = OpenLayers.Function.False;
            propagate = !this.stopDown;
        } else {
            this.started = false;
            this.start = null;
            this.last = null;
        }
        return propagate;
    },
    dragmove: function (evt) {
        this.lastMoveEvt = evt;
        if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) {
            if (this.documentDrag === true && this.documentEvents) {
                if (evt.element === document) {
                    this.adjustXY(evt);
                    this.setEvent(evt);
                } else {
                    this.removeDocumentEvents();
                }
            }
            if (this.interval > 0) {
                this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval);
            }
            this.dragging = true;
            this.move(evt);
            this.callback("move", [evt.xy]);
            if (!this.oldOnselectstart) {
                this.oldOnselectstart = document.onselectstart;
                document.onselectstart = OpenLayers.Function.False;
            }
            this.last = evt.xy;
        }
        return true;
    },
    dragend: function (evt) {
        if (this.started) {
            if (this.documentDrag === true && this.documentEvents) {
                this.adjustXY(evt);
                this.removeDocumentEvents();
            }
            var dragged = (this.start != this.last);
            this.started = false;
            this.dragging = false;
            OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown");
            this.up(evt);
            this.callback("up", [evt.xy]);
            if (dragged) {
                this.callback("done", [evt.xy]);
            }
            document.onselectstart = this.oldOnselectstart;
        }
        return true;
    },
    down: function (evt) {},
    move: function (evt) {},
    up: function (evt) {},
    out: function (evt) {},
    mousedown: function (evt) {
        return this.dragstart(evt);
    },
    touchstart: function (evt) {
        if (!this.touch) {
            this.touch = true;
            this.map.events.un({
                mousedown: this.mousedown,
                mouseup: this.mouseup,
                mousemove: this.mousemove,
                click: this.click,
                scope: this
            });
        }
        return this.dragstart(evt);
    },
    mousemove: function (evt) {
        return this.dragmove(evt);
    },
    touchmove: function (evt) {
        return this.dragmove(evt);
    },
    removeTimeout: function () {
        this.timeoutId = null;
        if (this.dragging) {
            this.mousemove(this.lastMoveEvt);
        }
    },
    mouseup: function (evt) {
        return this.dragend(evt);
    },
    touchend: function (evt) {
        evt.xy = this.last;
        return this.dragend(evt);
    },
    mouseout: function (evt) {
        if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
            if (this.documentDrag === true) {
                this.addDocumentEvents();
            } else {
                var dragged = (this.start != this.last);
                this.started = false;
                this.dragging = false;
                OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown");
                this.out(evt);
                this.callback("out", []);
                if (dragged) {
                    this.callback("done", [evt.xy]);
                }
                if (document.onselectstart) {
                    document.onselectstart = this.oldOnselectstart;
                }
            }
        }
        return true;
    },
    click: function (evt) {
        return (this.start == this.last);
    },
    activate: function () {
        var activated = false;
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.dragging = false;
            activated = true;
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.touch = false;
            this.started = false;
            this.dragging = false;
            this.start = null;
            this.last = null;
            deactivated = true;
            OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown");
        }
        return deactivated;
    },
    adjustXY: function (evt) {
        var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv);
        evt.xy.x -= pos[0];
        evt.xy.y -= pos[1];
    },
    addDocumentEvents: function () {
        OpenLayers.Element.addClass(document.body, "olDragDown");
        this.documentEvents = true;
        OpenLayers.Event.observe(document, "mousemove", this._docMove);
        OpenLayers.Event.observe(document, "mouseup", this._docUp);
    },
    removeDocumentEvents: function () {
        OpenLayers.Element.removeClass(document.body, "olDragDown");
        this.documentEvents = false;
        OpenLayers.Event.stopObserving(document, "mousemove", this._docMove);
        OpenLayers.Event.stopObserving(document, "mouseup", this._docUp);
    },
    CLASS_NAME: "OpenLayers.Handler.Drag"
});
OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, {
    EVENTMAP: {
        'click': {
            'in': 'click',
            'out': 'clickout'
        },
        'mousemove': {
            'in': 'over',
            'out': 'out'
        },
        'dblclick': {
            'in': 'dblclick',
            'out': null
        },
        'mousedown': {
            'in': null,
            'out': null
        },
        'mouseup': {
            'in': null,
            'out': null
        },
        'touchstart': {
            'in': 'click',
            'out': 'clickout'
        }
    },
    feature: null,
    lastFeature: null,
    down: null,
    up: null,
    touch: false,
    clickTolerance: 4,
    geometryTypes: null,
    stopClick: true,
    stopDown: true,
    stopUp: false,
    initialize: function (control, layer, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]);
        this.layer = layer;
    },
    touchstart: function (evt) {
        if (!this.touch) {
            this.touch = true;
            this.map.events.un({
                mousedown: this.mousedown,
                mouseup: this.mouseup,
                mousemove: this.mousemove,
                click: this.click,
                dblclick: this.dblclick,
                scope: this
            });
        }
        return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt);
    },
    touchmove: function (evt) {
        OpenLayers.Event.stop(evt);
    },
    mousedown: function (evt) {
        this.down = evt.xy;
        return this.handle(evt) ? !this.stopDown : true;
    },
    mouseup: function (evt) {
        this.up = evt.xy;
        return this.handle(evt) ? !this.stopUp : true;
    },
    click: function (evt) {
        return this.handle(evt) ? !this.stopClick : true;
    },
    mousemove: function (evt) {
        if (!this.callbacks['over'] && !this.callbacks['out']) {
            return true;
        }
        this.handle(evt);
        return true;
    },
    dblclick: function (evt) {
        return !this.handle(evt);
    },
    geometryTypeMatches: function (feature) {
        return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1;
    },
    handle: function (evt) {
        if (this.feature && !this.feature.layer) {
            this.feature = null;
        }
        var type = evt.type;
        var handled = false;
        var previouslyIn = !! (this.feature);
        var click = (type == "click" || type == "dblclick" || type == "touchstart");
        this.feature = this.layer.getFeatureFromEvent(evt);
        if (this.feature && !this.feature.layer) {
            this.feature = null;
        }
        if (this.lastFeature && !this.lastFeature.layer) {
            this.lastFeature = null;
        }
        if (this.feature) {
            if (type === "touchstart") {
                OpenLayers.Event.stop(evt);
            }
            var inNew = (this.feature != this.lastFeature);
            if (this.geometryTypeMatches(this.feature)) {
                if (previouslyIn && inNew) {
                    if (this.lastFeature) {
                        this.triggerCallback(type, 'out', [this.lastFeature]);
                    }
                    this.triggerCallback(type, 'in', [this.feature]);
                } else if (!previouslyIn || click) {
                    this.triggerCallback(type, 'in', [this.feature]);
                }
                this.lastFeature = this.feature;
                handled = true;
            } else {
                if (this.lastFeature && (previouslyIn && inNew || click)) {
                    this.triggerCallback(type, 'out', [this.lastFeature]);
                }
                this.feature = null;
            }
        } else {
            if (this.lastFeature && (previouslyIn || click)) {
                this.triggerCallback(type, 'out', [this.lastFeature]);
            }
        }
        return handled;
    },
    triggerCallback: function (type, mode, args) {
        var key = this.EVENTMAP[type][mode];
        if (key) {
            if (type == 'click' && this.up && this.down) {
                var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2));
                if (dpx <= this.clickTolerance) {
                    this.callback(key, args);
                }
            } else {
                this.callback(key, args);
            }
        }
    },
    activate: function () {
        var activated = false;
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.moveLayerToTop();
            this.map.events.on({
                "removelayer": this.handleMapEvents,
                "changelayer": this.handleMapEvents,
                scope: this
            });
            activated = true;
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.moveLayerBack();
            this.feature = null;
            this.lastFeature = null;
            this.down = null;
            this.up = null;
            this.touch = false;
            this.map.events.un({
                "removelayer": this.handleMapEvents,
                "changelayer": this.handleMapEvents,
                scope: this
            });
            deactivated = true;
        }
        return deactivated;
    },
    handleMapEvents: function (evt) {
        if (evt.type == "removelayer" || evt.property == "order") {
            this.moveLayerToTop();
        }
    },
    moveLayerToTop: function () {
        var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, this.layer.getZIndex()) + 1;
        this.layer.setZIndex(index);
    },
    moveLayerBack: function () {
        var index = this.layer.getZIndex() - 1;
        if (index >= this.map.Z_INDEX_BASE['Feature']) {
            this.layer.setZIndex(index);
        } else {
            this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer));
        }
    },
    CLASS_NAME: "OpenLayers.Handler.Feature"
});
OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, {
    geometryTypes: null,
    onStart: function (feature, pixel) {},
    onDrag: function (feature, pixel) {},
    onComplete: function (feature, pixel) {},
    onEnter: function (feature) {},
    onLeave: function (feature) {},
    documentDrag: false,
    layer: null,
    feature: null,
    dragCallbacks: {},
    featureCallbacks: {},
    lastPixel: null,
    initialize: function (layer, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        this.handlers = {
            drag: new OpenLayers.Handler.Drag(this, OpenLayers.Util.extend({
                down: this.downFeature,
                move: this.moveFeature,
                up: this.upFeature,
                out: this.cancel,
                done: this.doneDragging
            }, this.dragCallbacks), {
                documentDrag: this.documentDrag
            }),
            feature: new OpenLayers.Handler.Feature(this, this.layer, OpenLayers.Util.extend({
                click: this.clickFeature,
                clickout: this.clickoutFeature,
                over: this.overFeature,
                out: this.outFeature
            }, this.featureCallbacks), {
                geometryTypes: this.geometryTypes
            })
        };
    },
    clickFeature: function (feature) {
        if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) {
            this.handlers.drag.dragstart(this.handlers.feature.evt);
            this.handlers.drag.stopDown = false;
        }
    },
    clickoutFeature: function (feature) {
        if (this.handlers.feature.touch && this.over) {
            this.outFeature(feature);
            this.handlers.drag.stopDown = true;
        }
    },
    destroy: function () {
        this.layer = null;
        OpenLayers.Control.prototype.destroy.apply(this, []);
    },
    activate: function () {
        return (this.handlers.feature.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments));
    },
    deactivate: function () {
        this.handlers.drag.deactivate();
        this.handlers.feature.deactivate();
        this.feature = null;
        this.dragging = false;
        this.lastPixel = null;
        OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + "Over");
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    overFeature: function (feature) {
        var activated = false;
        if (!this.handlers.drag.dragging) {
            this.feature = feature;
            this.handlers.drag.activate();
            activated = true;
            this.over = true;
            OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
            this.onEnter(feature);
        } else {
            if (this.feature.id == feature.id) {
                this.over = true;
            } else {
                this.over = false;
            }
        }
        return activated;
    },
    downFeature: function (pixel) {
        this.lastPixel = pixel;
        this.onStart(this.feature, pixel);
    },
    moveFeature: function (pixel) {
        var res = this.map.getResolution();
        this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), res * (this.lastPixel.y - pixel.y));
        this.layer.drawFeature(this.feature);
        this.lastPixel = pixel;
        this.onDrag(this.feature, pixel);
    },
    upFeature: function (pixel) {
        if (!this.over) {
            this.handlers.drag.deactivate();
        }
    },
    doneDragging: function (pixel) {
        this.onComplete(this.feature, pixel);
    },
    outFeature: function (feature) {
        if (!this.handlers.drag.dragging) {
            this.over = false;
            this.handlers.drag.deactivate();
            OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + "Over");
            this.onLeave(feature);
            this.feature = null;
        } else {
            if (this.feature.id == feature.id) {
                this.over = false;
            }
        }
    },
    cancel: function () {
        this.handlers.drag.deactivate();
        this.over = false;
    },
    setMap: function (map) {
        this.handlers.drag.setMap(map);
        this.handlers.feature.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.DragFeature"
});
OpenLayers.StyleMap = OpenLayers.Class({
    styles: null,
    extendDefault: true,
    initialize: function (style, options) {
        this.styles = {
            "default": new OpenLayers.Style(OpenLayers.Feature.Vector.style["default"]),
            "select": new OpenLayers.Style(OpenLayers.Feature.Vector.style["select"]),
            "temporary": new OpenLayers.Style(OpenLayers.Feature.Vector.style["temporary"]),
            "delete": new OpenLayers.Style(OpenLayers.Feature.Vector.style["delete"])
        };
        if (style instanceof OpenLayers.Style) {
            this.styles["default"] = style;
            this.styles["select"] = style;
            this.styles["temporary"] = style;
            this.styles["delete"] = style;
        } else if (typeof style == "object") {
            for (var key in style) {
                if (style[key] instanceof OpenLayers.Style) {
                    this.styles[key] = style[key];
                } else if (typeof style[key] == "object") {
                    this.styles[key] = new OpenLayers.Style(style[key]);
                } else {
                    this.styles["default"] = new OpenLayers.Style(style);
                    this.styles["select"] = new OpenLayers.Style(style);
                    this.styles["temporary"] = new OpenLayers.Style(style);
                    this.styles["delete"] = new OpenLayers.Style(style);
                    break;
                }
            }
        }
        OpenLayers.Util.extend(this, options);
    },
    destroy: function () {
        for (var key in this.styles) {
            this.styles[key].destroy();
        }
        this.styles = null;
    },
    createSymbolizer: function (feature, intent) {
        if (!feature) {
            feature = new OpenLayers.Feature.Vector();
        }
        if (!this.styles[intent]) {
            intent = "default";
        }
        feature.renderIntent = intent;
        var defaultSymbolizer = {};
        if (this.extendDefault && intent != "default") {
            defaultSymbolizer = this.styles["default"].createSymbolizer(feature);
        }
        return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature));
    },
    addUniqueValueRules: function (renderIntent, property, symbolizers, context) {
        var rules = [];
        for (var value in symbolizers) {
            rules.push(new OpenLayers.Rule({
                symbolizer: symbolizers[value],
                context: context,
                filter: new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
                    property: property,
                    value: value
                })
            }));
        }
        this.styles[renderIntent].addRules(rules);
    },
    CLASS_NAME: "OpenLayers.StyleMap"
});
OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, {
    EVENT_TYPES: ["beforefeatureadded", "beforefeaturesadded", "featureadded", "featuresadded", "beforefeatureremoved", "beforefeaturesremoved", "featureremoved", "featuresremoved", "beforefeatureselected", "featureselected", "featureunselected", "beforefeaturemodified", "featuremodified", "afterfeaturemodified", "vertexmodified", "vertexremoved", "sketchstarted", "sketchmodified", "sketchcomplete", "refresh"],
    isBaseLayer: false,
    isFixed: false,
    features: null,
    filter: null,
    selectedFeatures: null,
    unrenderedFeatures: null,
    reportError: true,
    style: null,
    styleMap: null,
    strategies: null,
    protocol: null,
    renderers: ['SVG', 'VML', 'Canvas'],
    renderer: null,
    rendererOptions: null,
    geometryType: null,
    drawn: false,
    initialize: function (name, options) {
        this.EVENT_TYPES = OpenLayers.Layer.Vector.prototype.EVENT_TYPES.concat(OpenLayers.Layer.prototype.EVENT_TYPES);
        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
        if (!this.renderer || !this.renderer.supported()) {
            this.assignRenderer();
        }
        if (!this.renderer || !this.renderer.supported()) {
            this.renderer = null;
            this.displayError();
        }
        if (!this.styleMap) {
            this.styleMap = new OpenLayers.StyleMap();
        }
        this.features = [];
        this.selectedFeatures = [];
        this.unrenderedFeatures = {};
        if (this.strategies) {
            for (var i = 0, len = this.strategies.length; i < len; i++) {
                this.strategies[i].setLayer(this);
            }
        }
    },
    destroy: function () {
        if (this.strategies) {
            var strategy, i, len;
            for (i = 0, len = this.strategies.length; i < len; i++) {
                strategy = this.strategies[i];
                if (strategy.autoDestroy) {
                    strategy.destroy();
                }
            }
            this.strategies = null;
        }
        if (this.protocol) {
            if (this.protocol.autoDestroy) {
                this.protocol.destroy();
            }
            this.protocol = null;
        }
        this.destroyFeatures();
        this.features = null;
        this.selectedFeatures = null;
        this.unrenderedFeatures = null;
        if (this.renderer) {
            this.renderer.destroy();
        }
        this.renderer = null;
        this.geometryType = null;
        this.drawn = null;
        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.Vector(this.name, this.getOptions());
        }
        obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]);
        var features = this.features;
        var len = features.length;
        var clonedFeatures = new Array(len);
        for (var i = 0; i < len; ++i) {
            clonedFeatures[i] = features[i].clone();
        }
        obj.features = clonedFeatures;
        return obj;
    },
    refresh: function (obj) {
        if (this.calculateInRange() && this.visibility) {
            this.events.triggerEvent("refresh", obj);
        }
    },
    assignRenderer: function () {
        for (var i = 0, len = this.renderers.length; i < len; i++) {
            var rendererClass = this.renderers[i];
            var renderer = (typeof rendererClass == "function") ? rendererClass : OpenLayers.Renderer[rendererClass];
            if (renderer && renderer.prototype.supported()) {
                this.renderer = new renderer(this.div, this.rendererOptions);
                break;
            }
        }
    },
    displayError: function () {
        if (this.reportError) {
            OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", {
                'renderers': this.renderers.join("\n")
            }));
        }
    },
    setMap: function (map) {
        OpenLayers.Layer.prototype.setMap.apply(this, arguments);
        if (!this.renderer) {
            this.map.removeLayer(this);
        } else {
            this.renderer.map = this.map;
            this.renderer.setSize(this.map.getSize());
        }
    },
    afterAdd: function () {
        if (this.strategies) {
            var strategy, i, len;
            for (i = 0, len = this.strategies.length; i < len; i++) {
                strategy = this.strategies[i];
                if (strategy.autoActivate) {
                    strategy.activate();
                }
            }
        }
    },
    removeMap: function (map) {
        this.drawn = false;
        if (this.strategies) {
            var strategy, i, len;
            for (i = 0, len = this.strategies.length; i < len; i++) {
                strategy = this.strategies[i];
                if (strategy.autoActivate) {
                    strategy.deactivate();
                }
            }
        }
    },
    onMapResize: function () {
        OpenLayers.Layer.prototype.onMapResize.apply(this, arguments);
        this.renderer.setSize(this.map.getSize());
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
        var ng = (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG);
        if (ng) {
            dragging || this.renderer.updateDimensions(zoomChanged);
        } else {
            var coordSysUnchanged = true;
            if (!dragging) {
                this.renderer.root.style.visibility = "hidden";
                this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
                this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
                var extent = this.map.getExtent();
                coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
                this.renderer.root.style.visibility = "visible";
                if (OpenLayers.IS_GECKO === true) {
                    this.div.scrollLeft = this.div.scrollLeft;
                }
                if (!zoomChanged && coordSysUnchanged) {
                    for (var i in this.unrenderedFeatures) {
                        var feature = this.unrenderedFeatures[i];
                        this.drawFeature(feature);
                    }
                }
            }
        }
        if (!this.drawn || (!ng && (zoomChanged || !coordSysUnchanged))) {
            this.drawn = true;
            var feature;
            for (var i = 0, len = this.features.length; i < len; i++) {
                this.renderer.locked = (i !== (len - 1));
                feature = this.features[i];
                this.drawFeature(feature);
            }
        }
    },
    redraw: function () {
        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
            this.drawn = false;
        }
        return OpenLayers.Layer.prototype.redraw.apply(this, arguments);
    },
    display: function (display) {
        OpenLayers.Layer.prototype.display.apply(this, arguments);
        var currentDisplay = this.div.style.display;
        if (currentDisplay != this.renderer.root.style.display) {
            this.renderer.root.style.display = currentDisplay;
        }
    },
    addFeatures: function (features, options) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        var notify = !options || !options.silent;
        if (notify) {
            var event = {
                features: features
            };
            var ret = this.events.triggerEvent("beforefeaturesadded", event);
            if (ret === false) {
                return;
            }
            features = event.features;
        }
        var featuresAdded = [];
        for (var i = 0, len = features.length; i < len; i++) {
            if (i != (features.length - 1)) {
                this.renderer.locked = true;
            } else {
                this.renderer.locked = false;
            }
            var feature = features[i];
            if (this.geometryType && !(feature.geometry instanceof this.geometryType)) {
                var throwStr = OpenLayers.i18n('componentShouldBe', {
                    'geomType': this.geometryType.prototype.CLASS_NAME
                });
                throw throwStr;
            }
            feature.layer = this;
            if (!feature.style && this.style) {
                feature.style = OpenLayers.Util.extend({}, this.style);
            }
            if (notify) {
                if (this.events.triggerEvent("beforefeatureadded", {
                    feature: feature
                }) === false) {
                    continue;
                }
                this.preFeatureInsert(feature);
            }
            featuresAdded.push(feature);
            this.features.push(feature);
            this.drawFeature(feature);
            if (notify) {
                this.events.triggerEvent("featureadded", {
                    feature: feature
                });
                this.onFeatureInsert(feature);
            }
        }
        if (notify) {
            this.events.triggerEvent("featuresadded", {
                features: featuresAdded
            });
        }
    },
    removeFeatures: function (features, options) {
        if (!features || features.length === 0) {
            return;
        }
        if (features === this.features) {
            return this.removeAllFeatures(options);
        }
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        if (features === this.selectedFeatures) {
            features = features.slice();
        }
        var notify = !options || !options.silent;
        if (notify) {
            this.events.triggerEvent("beforefeaturesremoved", {
                features: features
            });
        }
        for (var i = features.length - 1; i >= 0; i--) {
            if (i != 0 && features[i - 1].geometry) {
                this.renderer.locked = true;
            } else {
                this.renderer.locked = false;
            }
            var feature = features[i];
            delete this.unrenderedFeatures[feature.id];
            if (notify) {
                this.events.triggerEvent("beforefeatureremoved", {
                    feature: feature
                });
            }
            this.features = OpenLayers.Util.removeItem(this.features, feature);
            feature.layer = null;
            if (feature.geometry) {
                this.renderer.eraseFeatures(feature);
            }
            if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) {
                OpenLayers.Util.removeItem(this.selectedFeatures, feature);
            }
            if (notify) {
                this.events.triggerEvent("featureremoved", {
                    feature: feature
                });
            }
        }
        if (notify) {
            this.events.triggerEvent("featuresremoved", {
                features: features
            });
        }
    },
    removeAllFeatures: function (options) {
        var notify = !options || !options.silent;
        var features = this.features;
        if (notify) {
            this.events.triggerEvent("beforefeaturesremoved", {
                features: features
            });
        }
        var feature;
        for (var i = features.length - 1; i >= 0; i--) {
            feature = features[i];
            if (notify) {
                this.events.triggerEvent("beforefeatureremoved", {
                    feature: feature
                });
            }
            feature.layer = null;
            if (notify) {
                this.events.triggerEvent("featureremoved", {
                    feature: feature
                });
            }
        }
        this.renderer.clear();
        this.features = [];
        this.unrenderedFeatures = {};
        this.selectedFeatures = [];
        if (notify) {
            this.events.triggerEvent("featuresremoved", {
                features: features
            });
        }
    },
    destroyFeatures: function (features, options) {
        var all = (features == undefined);
        if (all) {
            features = this.features;
        }
        if (features) {
            this.removeFeatures(features, options);
            for (var i = features.length - 1; i >= 0; i--) {
                features[i].destroy();
            }
        }
    },
    drawFeature: function (feature, style) {
        if (!this.drawn) {
            return;
        }
        if (typeof style != "object") {
            if (!style && feature.state === OpenLayers.State.DELETE) {
                style = "delete";
            }
            var renderIntent = style || feature.renderIntent;
            style = feature.style || this.style;
            if (!style) {
                style = this.styleMap.createSymbolizer(feature, renderIntent);
            }
        }
        var drawn = this.renderer.drawFeature(feature, style);
        if (drawn === false || drawn === null) {
            this.unrenderedFeatures[feature.id] = feature;
        } else {
            delete this.unrenderedFeatures[feature.id];
        }
    },
    eraseFeatures: function (features) {
        this.renderer.eraseFeatures(features);
    },
    getFeatureFromEvent: function (evt) {
        if (!this.renderer) {
            OpenLayers.Console.error(OpenLayers.i18n("getFeatureError"));
            return null;
        }
        var feature = null;
        var featureId = this.renderer.getFeatureIdFromEvent(evt);
        if (featureId) {
            if (typeof featureId === "string") {
                feature = this.getFeatureById(featureId);
            } else {
                feature = featureId;
            }
        }
        return feature;
    },
    getFeatureBy: function (property, value) {
        var feature = null;
        for (var i = 0, len = this.features.length; i < len; ++i) {
            if (this.features[i][property] == value) {
                feature = this.features[i];
                break;
            }
        }
        return feature;
    },
    getFeatureById: function (featureId) {
        return this.getFeatureBy('id', featureId);
    },
    getFeatureByFid: function (featureFid) {
        return this.getFeatureBy('fid', featureFid);
    },
    getFeaturesByAttribute: function (attrName, attrValue) {
        var i, feature, len = this.features.length,
            foundFeatures = [];
        for (i = 0; i < len; i++) {
            feature = this.features[i];
            if (feature && feature.attributes) {
                if (feature.attributes[attrName] === attrValue) {
                    foundFeatures.push(feature);
                }
            }
        }
        return foundFeatures;
    },
    onFeatureInsert: function (feature) {},
    preFeatureInsert: function (feature) {},
    getDataExtent: function () {
        var maxExtent = null;
        var features = this.features;
        if (features && (features.length > 0)) {
            var geometry = null;
            for (var i = 0, len = features.length; i < len; i++) {
                geometry = features[i].geometry;
                if (geometry) {
                    if (maxExtent === null) {
                        maxExtent = new OpenLayers.Bounds();
                    }
                    maxExtent.extend(geometry.getBounds());
                }
            }
        }
        return maxExtent;
    },
    CLASS_NAME: "OpenLayers.Layer.Vector"
});
OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, {
    displayInLayerSwitcher: false,
    layers: null,
    initialize: function (name, options) {
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, arguments);
    },
    display: function () {},
    getFeatureFromEvent: function (evt) {
        var layers = this.layers;
        var feature;
        for (var i = 0; i < layers.length; i++) {
            feature = layers[i].getFeatureFromEvent(evt);
            if (feature) {
                return feature;
            }
        }
    },
    setMap: function (map) {
        OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
        this.collectRoots();
        map.events.register("changelayer", this, this.handleChangeLayer);
    },
    removeMap: function (map) {
        map.events.unregister("changelayer", this, this.handleChangeLayer);
        this.resetRoots();
        OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
    },
    collectRoots: function () {
        var layer;
        for (var i = 0; i < this.map.layers.length; ++i) {
            layer = this.map.layers[i];
            if (OpenLayers.Util.indexOf(this.layers, layer) != -1) {
                layer.renderer.moveRoot(this.renderer);
            }
        }
    },
    resetRoots: function () {
        var layer;
        for (var i = 0; i < this.layers.length; ++i) {
            layer = this.layers[i];
            if (this.renderer && layer.renderer.getRenderLayerId() == this.id) {
                this.renderer.moveRoot(layer.renderer);
            }
        }
    },
    handleChangeLayer: function (evt) {
        var layer = evt.layer;
        if (evt.property == "order" && OpenLayers.Util.indexOf(this.layers, layer) != -1) {
            this.resetRoots();
            this.collectRoots();
        }
    },
    CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer"
});
OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["beforefeaturehighlighted", "featurehighlighted", "featureunhighlighted"],
    multipleKey: null,
    toggleKey: null,
    multiple: false,
    clickout: true,
    toggle: false,
    hover: false,
    highlightOnly: false,
    box: false,
    onBeforeSelect: function () {},
    onSelect: function () {},
    onUnselect: function () {},
    scope: null,
    geometryTypes: null,
    layer: null,
    layers: null,
    callbacks: null,
    selectStyle: null,
    renderIntent: "select",
    handlers: null,
    initialize: function (layers, options) {
        this.EVENT_TYPES = OpenLayers.Control.SelectFeature.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        if (this.scope === null) {
            this.scope = this;
        }
        this.initLayer(layers);
        var callbacks = {
            click: this.clickFeature,
            clickout: this.clickoutFeature
        };
        if (this.hover) {
            callbacks.over = this.overFeature;
            callbacks.out = this.outFeature;
        }
        this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
        this.handlers = {
            feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, {
                geometryTypes: this.geometryTypes
            })
        };
        if (this.box) {
            this.handlers.box = new OpenLayers.Handler.Box(this, {
                done: this.selectBox
            }, {
                boxDivClassName: "olHandlerBoxSelectFeature"
            });
        }
    },
    initLayer: function (layers) {
        if (OpenLayers.Util.isArray(layers)) {
            this.layers = layers;
            this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + "_container", {
                layers: layers
            });
        } else {
            this.layer = layers;
        }
    },
    destroy: function () {
        if (this.active && this.layers) {
            this.map.removeLayer(this.layer);
        }
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
        if (this.layers) {
            this.layer.destroy();
        }
    },
    activate: function () {
        if (!this.active) {
            if (this.layers) {
                this.map.addLayer(this.layer);
            }
            this.handlers.feature.activate();
            if (this.box && this.handlers.box) {
                this.handlers.box.activate();
            }
        }
        return OpenLayers.Control.prototype.activate.apply(this, arguments);
    },
    deactivate: function () {
        if (this.active) {
            this.handlers.feature.deactivate();
            if (this.handlers.box) {
                this.handlers.box.deactivate();
            }
            if (this.layers) {
                this.map.removeLayer(this.layer);
            }
        }
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    unselectAll: function (options) {
        var layers = this.layers || [this.layer];
        var layer, feature;
        for (var l = 0; l < layers.length; ++l) {
            layer = layers[l];
            for (var i = layer.selectedFeatures.length - 1; i >= 0; --i) {
                feature = layer.selectedFeatures[i];
                if (!options || options.except != feature) {
                    this.unselect(feature);
                }
            }
        }
    },
    clickFeature: function (feature) {
        if (!this.hover) {
            var selected = (OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1);
            if (selected) {
                if (this.toggleSelect()) {
                    this.unselect(feature);
                } else if (!this.multipleSelect()) {
                    this.unselectAll({
                        except: feature
                    });
                }
            } else {
                if (!this.multipleSelect()) {
                    this.unselectAll({
                        except: feature
                    });
                }
                this.select(feature);
            }
        }
    },
    multipleSelect: function () {
        return this.multiple || (this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey]);
    },
    toggleSelect: function () {
        return this.toggle || (this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey]);
    },
    clickoutFeature: function (feature) {
        if (!this.hover && this.clickout) {
            this.unselectAll();
        }
    },
    overFeature: function (feature) {
        var layer = feature.layer;
        if (this.hover) {
            if (this.highlightOnly) {
                this.highlight(feature);
            } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
                this.select(feature);
            }
        }
    },
    outFeature: function (feature) {
        if (this.hover) {
            if (this.highlightOnly) {
                if (feature._lastHighlighter == this.id) {
                    if (feature._prevHighlighter && feature._prevHighlighter != this.id) {
                        delete feature._lastHighlighter;
                        var control = this.map.getControl(feature._prevHighlighter);
                        if (control) {
                            control.highlight(feature);
                        }
                    } else {
                        this.unhighlight(feature);
                    }
                }
            } else {
                this.unselect(feature);
            }
        }
    },
    highlight: function (feature) {
        var layer = feature.layer;
        var cont = this.events.triggerEvent("beforefeaturehighlighted", {
            feature: feature
        });
        if (cont !== false) {
            feature._prevHighlighter = feature._lastHighlighter;
            feature._lastHighlighter = this.id;
            var style = this.selectStyle || this.renderIntent;
            layer.drawFeature(feature, style);
            this.events.triggerEvent("featurehighlighted", {
                feature: feature
            });
        }
    },
    unhighlight: function (feature) {
        var layer = feature.layer;
        if (feature._prevHighlighter == undefined) {
            delete feature._lastHighlighter;
        } else if (feature._prevHighlighter == this.id) {
            delete feature._prevHighlighter;
        } else {
            feature._lastHighlighter = feature._prevHighlighter;
            delete feature._prevHighlighter;
        }
        layer.drawFeature(feature, feature.style || feature.layer.style || "default");
        this.events.triggerEvent("featureunhighlighted", {
            feature: feature
        });
    },
    select: function (feature) {
        var cont = this.onBeforeSelect.call(this.scope, feature);
        var layer = feature.layer;
        if (cont !== false) {
            cont = layer.events.triggerEvent("beforefeatureselected", {
                feature: feature
            });
            if (cont !== false) {
                layer.selectedFeatures.push(feature);
                this.highlight(feature);
                if (!this.handlers.feature.lastFeature) {
                    this.handlers.feature.lastFeature = layer.selectedFeatures[0];
                }
                layer.events.triggerEvent("featureselected", {
                    feature: feature
                });
                this.onSelect.call(this.scope, feature);
            }
        }
    },
    unselect: function (feature) {
        var layer = feature.layer;
        this.unhighlight(feature);
        OpenLayers.Util.removeItem(layer.selectedFeatures, feature);
        layer.events.triggerEvent("featureunselected", {
            feature: feature
        });
        this.onUnselect.call(this.scope, feature);
    },
    selectBox: function (position) {
        if (position instanceof OpenLayers.Bounds) {
            var minXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.left, position.bottom));
            var maxXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.right, position.top));
            var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);
            if (!this.multipleSelect()) {
                this.unselectAll();
            }
            var prevMultiple = this.multiple;
            this.multiple = true;
            var layers = this.layers || [this.layer];
            var layer;
            for (var l = 0; l < layers.length; ++l) {
                layer = layers[l];
                for (var i = 0, len = layer.features.length; i < len; ++i) {
                    var feature = layer.features[i];
                    if (!feature.getVisibility()) {
                        continue;
                    }
                    if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) {
                        if (bounds.toGeometry().intersects(feature.geometry)) {
                            if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) {
                                this.select(feature);
                            }
                        }
                    }
                }
            }
            this.multiple = prevMultiple;
        }
    },
    setMap: function (map) {
        this.handlers.feature.setMap(map);
        if (this.box) {
            this.handlers.box.setMap(map);
        }
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    setLayer: function (layers) {
        var isActive = this.active;
        this.unselectAll();
        this.deactivate();
        if (this.layers) {
            this.layer.destroy();
            this.layers = null;
        }
        this.initLayer(layers);
        this.handlers.feature.layer = this.layer;
        if (isActive) {
            this.activate();
        }
    },
    CLASS_NAME: "OpenLayers.Control.SelectFeature"
});
OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, {
    KEY_EVENTS: ["keydown", "keyup"],
    eventListener: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
        this.eventListener = OpenLayers.Function.bindAsEventListener(this.handleKeyEvent, this);
    },
    destroy: function () {
        this.deactivate();
        this.eventListener = null;
        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
    },
    activate: function () {
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {
                OpenLayers.Event.observe(document, this.KEY_EVENTS[i], this.eventListener);
            }
            return true;
        } else {
            return false;
        }
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) {
                OpenLayers.Event.stopObserving(document, this.KEY_EVENTS[i], this.eventListener);
            }
            deactivated = true;
        }
        return deactivated;
    },
    handleKeyEvent: function (evt) {
        if (this.checkModifiers(evt)) {
            this.callback(evt.type, [evt]);
        }
    },
    CLASS_NAME: "OpenLayers.Handler.Keyboard"
});
OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, {
    geometryTypes: null,
    clickout: true,
    toggle: true,
    standalone: false,
    layer: null,
    feature: null,
    vertices: null,
    virtualVertices: null,
    selectControl: null,
    dragControl: null,
    handlers: null,
    deleteCodes: null,
    virtualStyle: null,
    vertexRenderIntent: null,
    mode: null,
    modified: false,
    radiusHandle: null,
    dragHandle: null,
    onModificationStart: function () {},
    onModification: function () {},
    onModificationEnd: function () {},
    initialize: function (layer, options) {
        options = options || {};
        this.layer = layer;
        this.vertices = [];
        this.virtualVertices = [];
        this.virtualStyle = OpenLayers.Util.extend({}, this.layer.style || this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent));
        this.virtualStyle.fillOpacity = 0.3;
        this.virtualStyle.strokeOpacity = 0.3;
        this.deleteCodes = [46, 68];
        this.mode = OpenLayers.Control.ModifyFeature.RESHAPE;
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        if (!(OpenLayers.Util.isArray(this.deleteCodes))) {
            this.deleteCodes = [this.deleteCodes];
        }
        var control = this;
        var selectOptions = {
            geometryTypes: this.geometryTypes,
            clickout: this.clickout,
            toggle: this.toggle,
            onBeforeSelect: this.beforeSelectFeature,
            onSelect: this.selectFeature,
            onUnselect: this.unselectFeature,
            scope: this
        };
        if (this.standalone === false) {
            this.selectControl = new OpenLayers.Control.SelectFeature(layer, selectOptions);
        }
        var dragOptions = {
            geometryTypes: ["OpenLayers.Geometry.Point"],
            snappingOptions: this.snappingOptions,
            onStart: function (feature, pixel) {
                control.dragStart.apply(control, [feature, pixel]);
            },
            onDrag: function (feature, pixel) {
                control.dragVertex.apply(control, [feature, pixel]);
            },
            onComplete: function (feature) {
                control.dragComplete.apply(control, [feature]);
            },
            featureCallbacks: {
                over: function (feature) {
                    if (control.standalone !== true || feature._sketch || control.feature === feature) {
                        control.dragControl.overFeature.apply(control.dragControl, [feature]);
                    }
                }
            }
        };
        this.dragControl = new OpenLayers.Control.DragFeature(layer, dragOptions);
        var keyboardOptions = {
            keydown: this.handleKeypress
        };
        this.handlers = {
            keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions)
        };
    },
    destroy: function () {
        this.layer = null;
        this.standalone || this.selectControl.destroy();
        this.dragControl.destroy();
        OpenLayers.Control.prototype.destroy.apply(this, []);
    },
    activate: function () {
        return ((this.standalone || this.selectControl.activate()) && this.handlers.keyboard.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments));
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.layer.removeFeatures(this.vertices, {
                silent: true
            });
            this.layer.removeFeatures(this.virtualVertices, {
                silent: true
            });
            this.vertices = [];
            this.dragControl.deactivate();
            var feature = this.feature;
            var valid = feature && feature.geometry && feature.layer;
            if (this.standalone === false) {
                if (valid) {
                    this.selectControl.unselect.apply(this.selectControl, [feature]);
                }
                this.selectControl.deactivate();
            } else {
                if (valid) {
                    this.unselectFeature(feature);
                }
            }
            this.handlers.keyboard.deactivate();
            deactivated = true;
        }
        return deactivated;
    },
    beforeSelectFeature: function (feature) {
        return this.layer.events.triggerEvent("beforefeaturemodified", {
            feature: feature
        });
    },
    selectFeature: function (feature) {
        if (!this.standalone || this.beforeSelectFeature(feature) !== false) {
            this.feature = feature;
            this.modified = false;
            this.resetVertices();
            this.dragControl.activate();
            this.onModificationStart(this.feature);
        }
        var modified = feature.modified;
        if (feature.geometry && !(modified && modified.geometry)) {
            this._originalGeometry = feature.geometry.clone();
        }
    },
    unselectFeature: function (feature) {
        this.layer.removeFeatures(this.vertices, {
            silent: true
        });
        this.vertices = [];
        this.layer.destroyFeatures(this.virtualVertices, {
            silent: true
        });
        this.virtualVertices = [];
        if (this.dragHandle) {
            this.layer.destroyFeatures([this.dragHandle], {
                silent: true
            });
            delete this.dragHandle;
        }
        if (this.radiusHandle) {
            this.layer.destroyFeatures([this.radiusHandle], {
                silent: true
            });
            delete this.radiusHandle;
        }
        this.feature = null;
        this.dragControl.deactivate();
        this.onModificationEnd(feature);
        this.layer.events.triggerEvent("afterfeaturemodified", {
            feature: feature,
            modified: this.modified
        });
        this.modified = false;
    },
    dragStart: function (feature, pixel) {
        if (feature != this.feature && !feature.geometry.parent && feature != this.dragHandle && feature != this.radiusHandle) {
            if (this.standalone === false && this.feature) {
                this.selectControl.clickFeature.apply(this.selectControl, [this.feature]);
            }
            if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) != -1) {
                this.standalone || this.selectControl.clickFeature.apply(this.selectControl, [feature]);
                this.dragControl.overFeature.apply(this.dragControl, [feature]);
                this.dragControl.lastPixel = pixel;
                this.dragControl.handlers.drag.started = true;
                this.dragControl.handlers.drag.start = pixel;
                this.dragControl.handlers.drag.last = pixel;
            }
        }
    },
    dragVertex: function (vertex, pixel) {
        this.modified = true;
        if (this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            if (this.feature != vertex) {
                this.feature = vertex;
            }
            this.layer.events.triggerEvent("vertexmodified", {
                vertex: vertex.geometry,
                feature: this.feature,
                pixel: pixel
            });
        } else {
            if (vertex._index) {
                vertex.geometry.parent.addComponent(vertex.geometry, vertex._index);
                delete vertex._index;
                OpenLayers.Util.removeItem(this.virtualVertices, vertex);
                this.vertices.push(vertex);
            } else if (vertex == this.dragHandle) {
                this.layer.removeFeatures(this.vertices, {
                    silent: true
                });
                this.vertices = [];
                if (this.radiusHandle) {
                    this.layer.destroyFeatures([this.radiusHandle], {
                        silent: true
                    });
                    this.radiusHandle = null;
                }
            } else if (vertex !== this.radiusHandle) {
                this.layer.events.triggerEvent("vertexmodified", {
                    vertex: vertex.geometry,
                    feature: this.feature,
                    pixel: pixel
                });
            }
            if (this.virtualVertices.length > 0) {
                this.layer.destroyFeatures(this.virtualVertices, {
                    silent: true
                });
                this.virtualVertices = [];
            }
            this.layer.drawFeature(this.feature, this.standalone ? undefined : this.selectControl.renderIntent);
        }
        this.layer.drawFeature(vertex);
    },
    dragComplete: function (vertex) {
        this.resetVertices();
        this.setFeatureState();
        this.onModification(this.feature);
        this.layer.events.triggerEvent("featuremodified", {
            feature: this.feature
        });
    },
    setFeatureState: function () {
        if (this.feature.state != OpenLayers.State.INSERT && this.feature.state != OpenLayers.State.DELETE) {
            this.feature.state = OpenLayers.State.UPDATE;
            if (this.modified && this._originalGeometry) {
                var feature = this.feature;
                feature.modified = OpenLayers.Util.extend(feature.modified, {
                    geometry: this._originalGeometry
                });
                delete this._originalGeometry;
            }
        }
    },
    resetVertices: function () {
        if (this.dragControl.feature) {
            this.dragControl.outFeature(this.dragControl.feature);
        }
        if (this.vertices.length > 0) {
            this.layer.removeFeatures(this.vertices, {
                silent: true
            });
            this.vertices = [];
        }
        if (this.virtualVertices.length > 0) {
            this.layer.removeFeatures(this.virtualVertices, {
                silent: true
            });
            this.virtualVertices = [];
        }
        if (this.dragHandle) {
            this.layer.destroyFeatures([this.dragHandle], {
                silent: true
            });
            this.dragHandle = null;
        }
        if (this.radiusHandle) {
            this.layer.destroyFeatures([this.radiusHandle], {
                silent: true
            });
            this.radiusHandle = null;
        }
        if (this.feature && this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
            if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
                this.collectDragHandle();
            }
            if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | OpenLayers.Control.ModifyFeature.RESIZE))) {
                this.collectRadiusHandle();
            }
            if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) {
                if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) {
                    this.collectVertices();
                }
            }
        }
    },
    handleKeypress: function (evt) {
        var code = evt.keyCode;
        if (this.feature && OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) {
            var vertex = this.dragControl.feature;
            if (vertex && OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && !this.dragControl.handlers.drag.dragging && vertex.geometry.parent) {
                vertex.geometry.parent.removeComponent(vertex.geometry);
                this.layer.events.triggerEvent("vertexremoved", {
                    vertex: vertex.geometry,
                    feature: this.feature,
                    pixel: evt.xy
                });
                this.layer.drawFeature(this.feature, this.standalone ? undefined : this.selectControl.renderIntent);
                this.modified = true;
                this.resetVertices();
                this.setFeatureState();
                this.onModification(this.feature);
                this.layer.events.triggerEvent("featuremodified", {
                    feature: this.feature
                });
            }
        }
    },
    collectVertices: function () {
        this.vertices = [];
        this.virtualVertices = [];
        var control = this;

        function collectComponentVertices(geometry) {
            var i, vertex, component, len;
            if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
                vertex = new OpenLayers.Feature.Vector(geometry);
                vertex._sketch = true;
                vertex.renderIntent = control.vertexRenderIntent;
                control.vertices.push(vertex);
            } else {
                var numVert = geometry.components.length;
                if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
                    numVert -= 1;
                }
                for (i = 0; i < numVert; ++i) {
                    component = geometry.components[i];
                    if (component.CLASS_NAME == "OpenLayers.Geometry.Point") {
                        vertex = new OpenLayers.Feature.Vector(component);
                        vertex._sketch = true;
                        vertex.renderIntent = control.vertexRenderIntent;
                        control.vertices.push(vertex);
                    } else {
                        collectComponentVertices(component);
                    }
                }
                if (geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") {
                    for (i = 0, len = geometry.components.length; i < len - 1; ++i) {
                        var prevVertex = geometry.components[i];
                        var nextVertex = geometry.components[i + 1];
                        if (prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") {
                            var x = (prevVertex.x + nextVertex.x) / 2;
                            var y = (prevVertex.y + nextVertex.y) / 2;
                            var point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(x, y), null, control.virtualStyle);
                            point.geometry.parent = geometry;
                            point._index = i + 1;
                            point._sketch = true;
                            control.virtualVertices.push(point);
                        }
                    }
                }
            }
        }
        collectComponentVertices.call(this, this.feature.geometry);
        this.layer.addFeatures(this.virtualVertices, {
            silent: true
        });
        this.layer.addFeatures(this.vertices, {
            silent: true
        });
    },
    collectDragHandle: function () {
        var geometry = this.feature.geometry;
        var center = geometry.getBounds().getCenterLonLat();
        var originGeometry = new OpenLayers.Geometry.Point(center.lon, center.lat);
        var origin = new OpenLayers.Feature.Vector(originGeometry);
        originGeometry.move = function (x, y) {
            OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
            geometry.move(x, y);
        };
        origin._sketch = true;
        this.dragHandle = origin;
        this.layer.addFeatures([this.dragHandle], {
            silent: true
        });
    },
    collectRadiusHandle: function () {
        var geometry = this.feature.geometry;
        var bounds = geometry.getBounds();
        var center = bounds.getCenterLonLat();
        var originGeometry = new OpenLayers.Geometry.Point(center.lon, center.lat);
        var radiusGeometry = new OpenLayers.Geometry.Point(bounds.right, bounds.bottom);
        var radius = new OpenLayers.Feature.Vector(radiusGeometry);
        var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE);
        var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE);
        var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE);
        radiusGeometry.move = function (x, y) {
            OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
            var dx1 = this.x - originGeometry.x;
            var dy1 = this.y - originGeometry.y;
            var dx0 = dx1 - x;
            var dy0 = dy1 - y;
            if (rotate) {
                var a0 = Math.atan2(dy0, dx0);
                var a1 = Math.atan2(dy1, dx1);
                var angle = a1 - a0;
                angle *= 180 / Math.PI;
                geometry.rotate(angle, originGeometry);
            }
            if (resize) {
                var scale, ratio;
                if (reshape) {
                    scale = dy1 / dy0;
                    ratio = (dx1 / dx0) / scale;
                } else {
                    var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
                    var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
                    scale = l1 / l0;
                }
                geometry.resize(scale, originGeometry, ratio);
            }
        };
        radius._sketch = true;
        this.radiusHandle = radius;
        this.layer.addFeatures([this.radiusHandle], {
            silent: true
        });
    },
    setMap: function (map) {
        this.standalone || this.selectControl.setMap(map);
        this.dragControl.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.ModifyFeature"
});
OpenLayers.Control.ModifyFeature.RESHAPE = 1;
OpenLayers.Control.ModifyFeature.RESIZE = 2;
OpenLayers.Control.ModifyFeature.ROTATE = 4;
OpenLayers.Control.ModifyFeature.DRAG = 8;
OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    sphericalMercator: false,
    zoomOffset: 0,
    serverResolutions: null,
    initialize: function (name, url, options) {
        if (options && options.sphericalMercator || this.sphericalMercator) {
            options = OpenLayers.Util.extend({
                maxExtent: new OpenLayers.Bounds(-128 * 156543.03390625, -128 * 156543.03390625, 128 * 156543.03390625, 128 * 156543.03390625),
                maxResolution: 156543.03390625,
                numZoomLevels: 19,
                units: "m",
                projection: "EPSG:900913"
            }, options);
        }
        url = url || this.url;
        name = name || this.name;
        var newArguments = [name, url,
        {},
        options];
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        var xyz = this.getXYZ(bounds);
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            var s = '' + xyz.x + xyz.y + xyz.z;
            url = this.selectUrl(s, url);
        }
        return OpenLayers.String.format(url, xyz);
    },
    getXYZ: function (bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom() + this.zoomOffset;
        var limit = Math.pow(2, z);
        if (this.wrapDateLine) {
            x = ((x % limit) + limit) % limit;
        }
        return {
            'x': x,
            'y': y,
            'z': z
        };
    },
    setMap: function (map) {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        if (!this.tileOrigin) {
            this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.XYZ"
});
OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
    name: "OpenStreetMap",
    attribution: "Data CC-By-SA by <a href='http://openstreetmap.org/'>OpenStreetMap</a>",
    sphericalMercator: true,
    url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
        return obj;
    },
    wrapDateLine: true,
    CLASS_NAME: "OpenLayers.Layer.OSM"
});
OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
    serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, 0.07464553542435169],
    attributionTemplate: '<span class="olBingAttribution ${type}">' + '<div><a target="_blank" href="http://www.bing.com/maps/">' + '<img src="${logo}" /></a></div>${copyrights}' + '<a style="white-space: nowrap" target="_blank" ' + 'href="http://www.microsoft.com/maps/product/terms.html">' + 'Terms of Use</a></span>',
    metadata: null,
    type: "Road",
    metadataParams: null,
    initialize: function (options) {
        options = OpenLayers.Util.applyDefaults({
            sphericalMercator: true
        }, options);
        var name = options.name || "Bing " + (options.type || this.type);
        var newArgs = [name, null, options];
        OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs);
        this.loadMetadata();
    },
    loadMetadata: function () {
        this._callbackId = "_callback_" + this.id.replace(/\./g, "_");
        window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this);
        var params = OpenLayers.Util.applyDefaults({
            key: this.key,
            jsonp: this._callbackId,
            include: "ImageryProviders"
        }, this.metadataParams);
        var url = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.type + "?" + OpenLayers.Util.getParameterString(params);
        var script = document.createElement("script");
        script.type = "text/javascript";
        script.src = url;
        script.id = this._callbackId;
        document.getElementsByTagName("head")[0].appendChild(script);
    },
    initLayer: function () {
        var res = this.metadata.resourceSets[0].resources[0];
        var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
        this.url = [];
        for (var i = 0; i < res.imageUrlSubdomains.length; ++i) {
            this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
        };
        this.addOptions({
            maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution),
            zoomOffset: res.zoomMin,
            numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels)
        }, true);
    },
    getURL: function (bounds) {
        if (!this.url) {
            return OpenLayers.Util.getImagesLocation() + "blank.gif";
        }
        var xyz = this.getXYZ(bounds),
            x = xyz.x,
            y = xyz.y,
            z = xyz.z;
        var quadDigits = [];
        for (var i = z; i > 0; --i) {
            var digit = '0';
            var mask = 1 << (i - 1);
            if ((x & mask) != 0) {
                digit++;
            }
            if ((y & mask) != 0) {
                digit++;
                digit++;
            }
            quadDigits.push(digit);
        }
        var quadKey = quadDigits.join("");
        var url = this.selectUrl('' + x + y + z, this.url);
        return OpenLayers.String.format(url, {
            'quadkey': quadKey
        });
    },
    updateAttribution: function () {
        var metadata = this.metadata;
        if (!metadata || !this.map || !this.map.center) {
            return;
        }
        var res = metadata.resourceSets[0].resources[0];
        var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326"));
        var providers = res.imageryProviders,
            zoom = this.map.getZoom() + 1,
            copyrights = "",
            provider, i, ii, j, jj, bbox, coverage;
        for (i = 0, ii = providers.length; i < ii; ++i) {
            provider = providers[i];
            for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) {
                coverage = provider.coverageAreas[j];
                bbox = OpenLayers.Bounds.fromArray(coverage.bbox);
                if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) {
                    copyrights += provider.attribution + " ";
                }
            }
        }
        this.attribution = OpenLayers.String.format(this.attributionTemplate, {
            type: this.type.toLowerCase(),
            logo: metadata.brandLogoUri,
            copyrights: copyrights
        });
        this.map && this.map.events.triggerEvent("changelayer", {
            layer: this,
            property: "attribution"
        });
    },
    setMap: function () {
        OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments);
        this.updateAttribution();
        this.map.events.register("moveend", this, this.updateAttribution);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.Bing(this.options);
        }
        obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
        return obj;
    },
    destroy: function () {
        this.map && this.map.events.unregister("moveend", this, this.updateAttribution);
        OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Layer.Bing"
});
OpenLayers.Layer.Bing.processMetadata = function (metadata) {
    this.metadata = metadata;
    this.initLayer();
    var script = document.getElementById(this._callbackId);
    script.parentNode.removeChild(script);
    window[this._callbackId] = undefined;
    delete this._callbackId;
};
OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, {
    dx: null,
    dy: null,
    ratio: 1.5,
    maxFeatures: 250,
    rotation: 0,
    origin: null,
    gridBounds: null,
    initialize: function (config) {
        config = config || {};
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]);
    },
    setMap: function (map) {
        OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
        map.events.register("moveend", this, this.onMoveEnd);
    },
    removeMap: function (map) {
        map.events.unregister("moveend", this, this.onMoveEnd);
        OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments);
    },
    setRatio: function (ratio) {
        this.ratio = ratio;
        this.updateGrid(true);
    },
    setMaxFeatures: function (maxFeatures) {
        this.maxFeatures = maxFeatures;
        this.updateGrid(true);
    },
    setSpacing: function (dx, dy) {
        this.dx = dx;
        this.dy = dy || dx;
        this.updateGrid(true);
    },
    setOrigin: function (origin) {
        this.origin = origin;
        this.updateGrid(true);
    },
    getOrigin: function () {
        if (!this.origin) {
            this.origin = this.map.getExtent().getCenterLonLat();
        }
        return this.origin;
    },
    setRotation: function (rotation) {
        this.rotation = rotation;
        this.updateGrid(true);
    },
    onMoveEnd: function () {
        this.updateGrid();
    },
    getViewBounds: function () {
        var bounds = this.map.getExtent();
        if (this.rotation) {
            var origin = this.getOrigin();
            var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
            var rect = bounds.toGeometry();
            rect.rotate(-this.rotation, rotationOrigin);
            bounds = rect.getBounds();
        }
        return bounds;
    },
    updateGrid: function (force) {
        if (force || this.invalidBounds()) {
            var viewBounds = this.getViewBounds();
            var origin = this.getOrigin();
            var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat);
            var viewBoundsWidth = viewBounds.getWidth();
            var viewBoundsHeight = viewBounds.getHeight();
            var aspectRatio = viewBoundsWidth / viewBoundsHeight;
            var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio);
            var maxWidth = maxHeight * aspectRatio;
            var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth);
            var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight);
            var center = viewBounds.getCenterLonLat();
            this.gridBounds = new OpenLayers.Bounds(center.lon - (gridWidth / 2), center.lat - (gridHeight / 2), center.lon + (gridWidth / 2), center.lat + (gridHeight / 2));
            var rows = Math.floor(gridHeight / this.dy);
            var cols = Math.floor(gridWidth / this.dx);
            var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx));
            var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy));
            var features = new Array(rows * cols);
            var x, y, point;
            for (var i = 0; i < cols; ++i) {
                x = gridLeft + (i * this.dx);
                for (var j = 0; j < rows; ++j) {
                    y = gridBottom + (j * this.dy);
                    point = new OpenLayers.Geometry.Point(x, y);
                    if (this.rotation) {
                        point.rotate(this.rotation, rotationOrigin);
                    }
                    features[(i * rows) + j] = new OpenLayers.Feature.Vector(point);
                }
            }
            this.destroyFeatures(this.features, {
                silent: true
            });
            this.addFeatures(features, {
                silent: true
            });
        }
    },
    invalidBounds: function () {
        return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds());
    },
    CLASS_NAME: "OpenLayers.Layer.PointGrid"
});
OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, {
    wheelListener: null,
    mousePosition: null,
    interval: 0,
    delta: 0,
    cumulative: true,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
        this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this);
    },
    destroy: function () {
        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
        this.wheelListener = null;
    },
    onWheelEvent: function (e) {
        if (!this.map || !this.checkModifiers(e)) {
            return;
        }
        var overScrollableDiv = false;
        var overLayerDiv = false;
        var overMapDiv = false;
        var elem = OpenLayers.Event.element(e);
        while ((elem != null) && !overMapDiv && !overScrollableDiv) {
            if (!overScrollableDiv) {
                try {
                    if (elem.currentStyle) {
                        overflow = elem.currentStyle["overflow"];
                    } else {
                        var style = document.defaultView.getComputedStyle(elem, null);
                        var overflow = style.getPropertyValue("overflow");
                    }
                    overScrollableDiv = (overflow && (overflow == "auto") || (overflow == "scroll"));
                } catch (err) {}
            }
            if (!overLayerDiv) {
                for (var i = 0, len = this.map.layers.length; i < len; i++) {
                    if (elem == this.map.layers[i].div || elem == this.map.layers[i].pane) {
                        overLayerDiv = true;
                        break;
                    }
                }
            }
            overMapDiv = (elem == this.map.div);
            elem = elem.parentNode;
        }
        if (!overScrollableDiv && overMapDiv) {
            if (overLayerDiv) {
                var delta = 0;
                if (!e) {
                    e = window.event;
                }
                if (e.wheelDelta) {
                    delta = e.wheelDelta / 120;
                    if (window.opera && window.opera.version() < 9.2) {
                        delta = -delta;
                    }
                } else if (e.detail) {
                    delta = -e.detail / 3;
                }
                this.delta = this.delta + delta;
                if (this.interval) {
                    window.clearTimeout(this._timeoutId);
                    this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function () {
                        this.wheelZoom(e);
                    }, this), this.interval);
                } else {
                    this.wheelZoom(e);
                }
            }
            OpenLayers.Event.stop(e);
        }
    },
    wheelZoom: function (e) {
        var delta = this.delta;
        this.delta = 0;
        if (delta) {
            if (this.mousePosition) {
                e.xy = this.mousePosition;
            }
            if (!e.xy) {
                e.xy = this.map.getPixelFromLonLat(this.map.getCenter());
            }
            if (delta < 0) {
                this.callback("down", [e, this.cumulative ? delta : -1]);
            } else {
                this.callback("up", [e, this.cumulative ? delta : 1]);
            }
        }
    },
    mousemove: function (evt) {
        this.mousePosition = evt.xy;
    },
    activate: function (evt) {
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            var wheelListener = this.wheelListener;
            OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener);
            OpenLayers.Event.observe(window, "mousewheel", wheelListener);
            OpenLayers.Event.observe(document, "mousewheel", wheelListener);
            return true;
        } else {
            return false;
        }
    },
    deactivate: function (evt) {
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            var wheelListener = this.wheelListener;
            OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener);
            OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener);
            OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener);
            return true;
        } else {
            return false;
        }
    },
    CLASS_NAME: "OpenLayers.Handler.MouseWheel"
});
OpenLayers.Symbolizer = OpenLayers.Class({
    zIndex: 0,
    initialize: function (config) {
        OpenLayers.Util.extend(this, config);
    },
    clone: function () {
        var Type = eval(this.CLASS_NAME);
        return new Type(OpenLayers.Util.extend({}, this));
    },
    CLASS_NAME: "OpenLayers.Symbolizer"
});
OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, {
    initialize: function (config) {
        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Symbolizer.Raster"
});
OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, {
    initialize: function (config) {
        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Symbolizer.Point"
});
OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, {
    initialize: function (config) {
        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Symbolizer.Line"
});
OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, {
    initialize: function (config) {
        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Symbolizer.Polygon"
});
OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, {
    initialize: function (config) {
        OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Symbolizer.Text"
});
OpenLayers.Rule = OpenLayers.Class({
    id: null,
    name: null,
    title: null,
    description: null,
    context: null,
    filter: null,
    elseFilter: false,
    symbolizer: null,
    symbolizers: null,
    minScaleDenominator: null,
    maxScaleDenominator: null,
    initialize: function (options) {
        this.symbolizer = {};
        OpenLayers.Util.extend(this, options);
        if (this.symbolizers) {
            delete this.symbolizer;
        }
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    destroy: function () {
        for (var i in this.symbolizer) {
            this.symbolizer[i] = null;
        }
        this.symbolizer = null;
        delete this.symbolizers;
    },
    evaluate: function (feature) {
        var context = this.getContext(feature);
        var applies = true;
        if (this.minScaleDenominator || this.maxScaleDenominator) {
            var scale = feature.layer.map.getScale();
        }
        if (this.minScaleDenominator) {
            applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context);
        }
        if (applies && this.maxScaleDenominator) {
            applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context);
        }
        if (applies && this.filter) {
            if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") {
                applies = this.filter.evaluate(feature);
            } else {
                applies = this.filter.evaluate(context);
            }
        }
        return applies;
    },
    getContext: function (feature) {
        var context = this.context;
        if (!context) {
            context = feature.attributes || feature.data;
        }
        if (typeof this.context == "function") {
            context = this.context(feature);
        }
        return context;
    },
    clone: function () {
        var options = OpenLayers.Util.extend({}, this);
        if (this.symbolizers) {
            var len = this.symbolizers.length;
            options.symbolizers = new Array(len);
            for (var i = 0; i < len; ++i) {
                options.symbolizers[i] = this.symbolizers[i].clone();
            }
        } else {
            options.symbolizer = {};
            var value, type;
            for (var key in this.symbolizer) {
                value = this.symbolizer[key];
                type = typeof value;
                if (type === "object") {
                    options.symbolizer[key] = OpenLayers.Util.extend({}, value);
                } else if (type === "string") {
                    options.symbolizer[key] = value;
                }
            }
        }
        options.filter = this.filter && this.filter.clone();
        options.context = this.context && OpenLayers.Util.extend({}, this.context);
        return new OpenLayers.Rule(options);
    },
    CLASS_NAME: "OpenLayers.Rule"
});
OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, {
    type: null,
    property: null,
    value: null,
    distance: null,
    distanceUnits: null,
    initialize: function (options) {
        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
    },
    evaluate: function (feature) {
        var intersect = false;
        switch (this.type) {
        case OpenLayers.Filter.Spatial.BBOX:
        case OpenLayers.Filter.Spatial.INTERSECTS:
            if (feature.geometry) {
                var geom = this.value;
                if (this.value.CLASS_NAME == "OpenLayers.Bounds") {
                    geom = this.value.toGeometry();
                }
                if (feature.geometry.intersects(geom)) {
                    intersect = true;
                }
            }
            break;
        default:
            OpenLayers.Console.error(OpenLayers.i18n("filterEvaluateNotImplemented"));
            break;
        }
        return intersect;
    },
    clone: function () {
        var options = OpenLayers.Util.applyDefaults({
            value: this.value && this.value.clone && this.value.clone()
        }, this);
        return new OpenLayers.Filter.Spatial(options);
    },
    CLASS_NAME: "OpenLayers.Filter.Spatial"
});
OpenLayers.Filter.Spatial.BBOX = "BBOX";
OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS";
OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN";
OpenLayers.Filter.Spatial.WITHIN = "WITHIN";
OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS";
OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    stringifyOutput: true,
    namedLayersAsArray: false,
    CLASS_NAME: "OpenLayers.Format.SLD"
});
OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, {
    schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd",
    initialize: function (options) {
        OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "gml": OpenLayers.Util.applyDefaults({
            "outerBoundaryIs": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                container.outer = obj.components[0];
            },
            "innerBoundaryIs": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                container.inner.push(obj.components[0]);
            },
            "Box": function (node, container) {
                var obj = {};
                this.readChildNodes(node, obj);
                if (!container.components) {
                    container.components = [];
                }
                var min = obj.points[0];
                var max = obj.points[1];
                container.components.push(new OpenLayers.Bounds(min.x, min.y, max.x, max.y));
            }
        }, OpenLayers.Format.GML.Base.prototype.readers["gml"]),
        "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"],
        "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"]
    },
    write: function (features) {
        var name;
        if (OpenLayers.Util.isArray(features)) {
            name = "wfs:FeatureCollection";
        } else {
            name = "gml:featureMember";
        }
        var root = this.writeNode(name, features);
        this.setAttributeNS(root, this.namespaces["xsi"], "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    writers: {
        "gml": OpenLayers.Util.applyDefaults({
            "Point": function (geometry) {
                var node = this.createElementNSPlus("gml:Point");
                this.writeNode("coordinates", [geometry], node);
                return node;
            },
            "coordinates": function (points) {
                var numPoints = points.length;
                var parts = new Array(numPoints);
                var point;
                for (var i = 0; i < numPoints; ++i) {
                    point = points[i];
                    if (this.xy) {
                        parts[i] = point.x + "," + point.y;
                    } else {
                        parts[i] = point.y + "," + point.x;
                    }
                    if (point.z != undefined) {
                        parts[i] += "," + point.z;
                    }
                }
                return this.createElementNSPlus("gml:coordinates", {
                    attributes: {
                        decimal: ".",
                        cs: ",",
                        ts: " "
                    },
                    value: (numPoints == 1) ? parts[0] : parts.join(" ")
                });
            },
            "LineString": function (geometry) {
                var node = this.createElementNSPlus("gml:LineString");
                this.writeNode("coordinates", geometry.components, node);
                return node;
            },
            "Polygon": function (geometry) {
                var node = this.createElementNSPlus("gml:Polygon");
                this.writeNode("outerBoundaryIs", geometry.components[0], node);
                for (var i = 1; i < geometry.components.length; ++i) {
                    this.writeNode("innerBoundaryIs", geometry.components[i], node);
                }
                return node;
            },
            "outerBoundaryIs": function (ring) {
                var node = this.createElementNSPlus("gml:outerBoundaryIs");
                this.writeNode("LinearRing", ring, node);
                return node;
            },
            "innerBoundaryIs": function (ring) {
                var node = this.createElementNSPlus("gml:innerBoundaryIs");
                this.writeNode("LinearRing", ring, node);
                return node;
            },
            "LinearRing": function (ring) {
                var node = this.createElementNSPlus("gml:LinearRing");
                this.writeNode("coordinates", ring.components, node);
                return node;
            },
            "Box": function (bounds) {
                var node = this.createElementNSPlus("gml:Box");
                this.writeNode("coordinates", [{
                    x: bounds.left,
                    y: bounds.bottom
                }, {
                    x: bounds.right,
                    y: bounds.top
                }], node);
                if (this.srsName) {
                    node.setAttribute("srsName", this.srsName);
                }
                return node;
            }
        }, OpenLayers.Format.GML.Base.prototype.writers["gml"]),
        "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"],
        "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"]
    },
    CLASS_NAME: "OpenLayers.Format.GML.v2"
});
OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class(OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, {
    VERSION: "1.0.0",
    schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd",
    initialize: function (options) {
        OpenLayers.Format.GML.v2.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "ogc": OpenLayers.Util.applyDefaults({
            "PropertyIsEqualTo": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.EQUAL_TO
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsNotEqualTo": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO
                });
                this.readChildNodes(node, filter);
                obj.filters.push(filter);
            },
            "PropertyIsLike": function (node, obj) {
                var filter = new OpenLayers.Filter.Comparison({
                    type: OpenLayers.Filter.Comparison.LIKE
                });
                this.readChildNodes(node, filter);
                var wildCard = node.getAttribute("wildCard");
                var singleChar = node.getAttribute("singleChar");
                var esc = node.getAttribute("escape");
                filter.value2regex(wildCard, singleChar, esc);
                obj.filters.push(filter);
            }
        }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]),
        "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
        "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"]
    },
    writers: {
        "ogc": OpenLayers.Util.applyDefaults({
            "PropertyIsEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsNotEqualTo": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
                this.writeNode("PropertyName", filter, node);
                this.writeOgcExpression(filter.value, node);
                return node;
            },
            "PropertyIsLike": function (filter) {
                var node = this.createElementNSPlus("ogc:PropertyIsLike", {
                    attributes: {
                        wildCard: "*",
                        singleChar: ".",
                        escape: "!"
                    }
                });
                this.writeNode("PropertyName", filter, node);
                this.writeNode("Literal", filter.regex2value(), node);
                return node;
            },
            "BBOX": function (filter) {
                var node = this.createElementNSPlus("ogc:BBOX");
                filter.property && this.writeNode("PropertyName", filter, node);
                var box = this.writeNode("gml:Box", filter.value, node);
                if (filter.projection) {
                    box.setAttribute("srsName", filter.projection);
                }
                return node;
            }
        }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]),
        "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
        "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"]
    },
    writeSpatial: function (filter, name) {
        var node = this.createElementNSPlus("ogc:" + name);
        this.writeNode("PropertyName", filter, node);
        if (filter.value instanceof OpenLayers.Filter.Function) {
            this.writeNode("Function", filter.value, node);
        } else {
            var child;
            if (filter.value instanceof OpenLayers.Geometry) {
                child = this.writeNode("feature:_geometry", filter.value).firstChild;
            } else {
                child = this.writeNode("gml:Box", filter.value);
            }
            if (filter.projection) {
                child.setAttribute("srsName", filter.projection);
            }
            node.appendChild(child);
        }
        return node;
    },
    CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0"
});
OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, {
    version: "1.0.0",
    srsNameInQuery: false,
    schemaLocations: {
        "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd"
    },
    initialize: function (options) {
        OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
        OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]);
    },
    readNode: function (node, obj, first) {
        return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, [node, obj]);
    },
    readers: {
        "wfs": OpenLayers.Util.applyDefaults({
            "WFS_TransactionResponse": function (node, obj) {
                obj.insertIds = [];
                obj.success = false;
                this.readChildNodes(node, obj);
            },
            "InsertResult": function (node, container) {
                var obj = {
                    fids: []
                };
                this.readChildNodes(node, obj);
                container.insertIds.push(obj.fids[0]);
            },
            "TransactionResult": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Status": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "SUCCESS": function (node, obj) {
                obj.success = true;
            }
        }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]),
        "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"],
        "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"],
        "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"]
    },
    writers: {
        "wfs": OpenLayers.Util.applyDefaults({
            "Query": function (options) {
                options = OpenLayers.Util.extend({
                    featureNS: this.featureNS,
                    featurePrefix: this.featurePrefix,
                    featureType: this.featureType,
                    srsName: this.srsName,
                    srsNameInQuery: this.srsNameInQuery
                }, options);
                var prefix = options.featurePrefix;
                var node = this.createElementNSPlus("wfs:Query", {
                    attributes: {
                        typeName: (prefix ? prefix + ":" : "") + options.featureType
                    }
                });
                if (options.srsNameInQuery && options.srsName) {
                    node.setAttribute("srsName", options.srsName);
                }
                if (options.featureNS) {
                    node.setAttribute("xmlns:" + prefix, options.featureNS);
                }
                if (options.propertyNames) {
                    for (var i = 0, len = options.propertyNames.length; i < len; i++) {
                        this.writeNode("ogc:PropertyName", {
                            property: options.propertyNames[i]
                        }, node);
                    }
                }
                if (options.filter) {
                    this.setFilterProperty(options.filter);
                    this.writeNode("ogc:Filter", options.filter, node);
                }
                return node;
            }
        }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]),
        "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"],
        "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"],
        "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"]
    },
    CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0"
});
OpenLayers.ElementsIndexer = OpenLayers.Class({
    maxZIndex: null,
    order: null,
    indices: null,
    compare: null,
    initialize: function (yOrdering) {
        this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER;
        this.clear();
    },
    insert: function (newNode) {
        if (this.exists(newNode)) {
            this.remove(newNode);
        }
        var nodeId = newNode.id;
        this.determineZIndex(newNode);
        var leftIndex = -1;
        var rightIndex = this.order.length;
        var middle;
        while (rightIndex - leftIndex > 1) {
            middle = parseInt((leftIndex + rightIndex) / 2);
            var placement = this.compare(this, newNode, OpenLayers.Util.getElement(this.order[middle]));
            if (placement > 0) {
                leftIndex = middle;
            } else {
                rightIndex = middle;
            }
        }
        this.order.splice(rightIndex, 0, nodeId);
        this.indices[nodeId] = this.getZIndex(newNode);
        return this.getNextElement(rightIndex);
    },
    remove: function (node) {
        var nodeId = node.id;
        var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId);
        if (arrayIndex >= 0) {
            this.order.splice(arrayIndex, 1);
            delete this.indices[nodeId];
            if (this.order.length > 0) {
                var lastId = this.order[this.order.length - 1];
                this.maxZIndex = this.indices[lastId];
            } else {
                this.maxZIndex = 0;
            }
        }
    },
    clear: function () {
        this.order = [];
        this.indices = {};
        this.maxZIndex = 0;
    },
    exists: function (node) {
        return (this.indices[node.id] != null);
    },
    getZIndex: function (node) {
        return node._style.graphicZIndex;
    },
    determineZIndex: function (node) {
        var zIndex = node._style.graphicZIndex;
        if (zIndex == null) {
            zIndex = this.maxZIndex;
            node._style.graphicZIndex = zIndex;
        } else if (zIndex > this.maxZIndex) {
            this.maxZIndex = zIndex;
        }
    },
    getNextElement: function (index) {
        var nextIndex = index + 1;
        if (nextIndex < this.order.length) {
            var nextElement = OpenLayers.Util.getElement(this.order[nextIndex]);
            if (nextElement == undefined) {
                nextElement = this.getNextElement(nextIndex);
            }
            return nextElement;
        } else {
            return null;
        }
    },
    CLASS_NAME: "OpenLayers.ElementsIndexer"
});
OpenLayers.ElementsIndexer.IndexingMethods = {
    Z_ORDER: function (indexer, newNode, nextNode) {
        var newZIndex = indexer.getZIndex(newNode);
        var returnVal = 0;
        if (nextNode) {
            var nextZIndex = indexer.getZIndex(nextNode);
            returnVal = newZIndex - nextZIndex;
        }
        return returnVal;
    },
    Z_ORDER_DRAWING_ORDER: function (indexer, newNode, nextNode) {
        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);
        if (nextNode && returnVal == 0) {
            returnVal = 1;
        }
        return returnVal;
    },
    Z_ORDER_Y_ORDER: function (indexer, newNode, nextNode) {
        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode);
        if (nextNode && returnVal === 0) {
            var result = nextNode._boundsBottom - newNode._boundsBottom;
            returnVal = (result === 0) ? 1 : result;
        }
        return returnVal;
    }
};
OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, {
    rendererRoot: null,
    root: null,
    vectorRoot: null,
    textRoot: null,
    xmlns: null,
    indexer: null,
    BACKGROUND_ID_SUFFIX: "_background",
    LABEL_ID_SUFFIX: "_label",
    initialize: function (containerID, options) {
        OpenLayers.Renderer.prototype.initialize.apply(this, arguments);
        this.rendererRoot = this.createRenderRoot();
        this.root = this.createRoot("_root");
        this.vectorRoot = this.createRoot("_vroot");
        this.textRoot = this.createRoot("_troot");
        this.root.appendChild(this.vectorRoot);
        this.root.appendChild(this.textRoot);
        this.rendererRoot.appendChild(this.root);
        this.container.appendChild(this.rendererRoot);
        if (options && (options.zIndexing || options.yOrdering)) {
            this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering);
        }
    },
    destroy: function () {
        this.clear();
        this.rendererRoot = null;
        this.root = null;
        this.xmlns = null;
        OpenLayers.Renderer.prototype.destroy.apply(this, arguments);
    },
    clear: function () {
        var child;
        var root = this.vectorRoot;
        if (root) {
            while (child = root.firstChild) {
                root.removeChild(child);
            }
        }
        root = this.textRoot;
        if (root) {
            while (child = root.firstChild) {
                root.removeChild(child);
            }
        }
        if (this.indexer) {
            this.indexer.clear();
        }
    },
    getNodeType: function (geometry, style) {},
    drawGeometry: function (geometry, style, featureId) {
        var className = geometry.CLASS_NAME;
        var rendered = true;
        if ((className == "OpenLayers.Geometry.Collection") || (className == "OpenLayers.Geometry.MultiPoint") || (className == "OpenLayers.Geometry.MultiLineString") || (className == "OpenLayers.Geometry.MultiPolygon")) {
            for (var i = 0, len = geometry.components.length; i < len; i++) {
                rendered = this.drawGeometry(geometry.components[i], style, featureId) && rendered;
            }
            return rendered;
        };
        rendered = false;
        var removeBackground = false;
        if (style.display != "none") {
            if (style.backgroundGraphic) {
                this.redrawBackgroundNode(geometry.id, geometry, style, featureId);
            } else {
                removeBackground = true;
            }
            rendered = this.redrawNode(geometry.id, geometry, style, featureId);
        }
        if (rendered == false) {
            var node = document.getElementById(geometry.id);
            if (node) {
                if (node._style.backgroundGraphic) {
                    removeBackground = true;
                }
                node.parentNode.removeChild(node);
            }
        }
        if (removeBackground) {
            var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX);
            if (node) {
                node.parentNode.removeChild(node);
            }
        }
        return rendered;
    },
    redrawNode: function (id, geometry, style, featureId) {
        style = this.applyDefaultSymbolizer(style);
        var node = this.nodeFactory(id, this.getNodeType(geometry, style));
        node._featureId = featureId;
        node._boundsBottom = geometry.getBounds().bottom;
        node._geometryClass = geometry.CLASS_NAME;
        node._style = style;
        var drawResult = this.drawGeometryNode(node, geometry, style);
        if (drawResult === false) {
            return false;
        }
        node = drawResult.node;
        if (this.indexer) {
            var insert = this.indexer.insert(node);
            if (insert) {
                this.vectorRoot.insertBefore(node, insert);
            } else {
                this.vectorRoot.appendChild(node);
            }
        } else {
            if (node.parentNode !== this.vectorRoot) {
                this.vectorRoot.appendChild(node);
            }
        }
        this.postDraw(node);
        return drawResult.complete;
    },
    redrawBackgroundNode: function (id, geometry, style, featureId) {
        var backgroundStyle = OpenLayers.Util.extend({}, style);
        backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic;
        backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset;
        backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset;
        backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex;
        backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth;
        backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight;
        backgroundStyle.backgroundGraphic = null;
        backgroundStyle.backgroundXOffset = null;
        backgroundStyle.backgroundYOffset = null;
        backgroundStyle.backgroundGraphicZIndex = null;
        return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null);
    },
    drawGeometryNode: function (node, geometry, style) {
        style = style || node._style;
        var options = {
            'isFilled': style.fill === undefined ? true : style.fill,
            'isStroked': style.stroke === undefined ? !! style.strokeWidth : style.stroke
        };
        var drawn;
        switch (geometry.CLASS_NAME) {
        case "OpenLayers.Geometry.Point":
            if (style.graphic === false) {
                options.isFilled = false;
                options.isStroked = false;
            }
            drawn = this.drawPoint(node, geometry);
            break;
        case "OpenLayers.Geometry.LineString":
            options.isFilled = false;
            drawn = this.drawLineString(node, geometry);
            break;
        case "OpenLayers.Geometry.LinearRing":
            drawn = this.drawLinearRing(node, geometry);
            break;
        case "OpenLayers.Geometry.Polygon":
            drawn = this.drawPolygon(node, geometry);
            break;
        case "OpenLayers.Geometry.Surface":
            drawn = this.drawSurface(node, geometry);
            break;
        case "OpenLayers.Geometry.Rectangle":
            drawn = this.drawRectangle(node, geometry);
            break;
        default:
            break;
        }
        node._options = options;
        if (drawn != false) {
            return {
                node: this.setStyle(node, style, options, geometry),
                complete: drawn
            };
        } else {
            return false;
        }
    },
    postDraw: function (node) {},
    drawPoint: function (node, geometry) {},
    drawLineString: function (node, geometry) {},
    drawLinearRing: function (node, geometry) {},
    drawPolygon: function (node, geometry) {},
    drawRectangle: function (node, geometry) {},
    drawCircle: function (node, geometry) {},
    drawSurface: function (node, geometry) {},
    removeText: function (featureId) {
        var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX);
        if (label) {
            this.textRoot.removeChild(label);
        }
    },
    getFeatureIdFromEvent: function (evt) {
        var target = evt.target;
        var useElement = target && target.correspondingUseElement;
        var node = useElement ? useElement : (target || evt.srcElement);
        var featureId = node._featureId;
        return featureId;
    },
    eraseGeometry: function (geometry, featureId) {
        if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") || (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") || (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") || (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) {
            for (var i = 0, len = geometry.components.length; i < len; i++) {
                this.eraseGeometry(geometry.components[i], featureId);
            }
        } else {
            var element = OpenLayers.Util.getElement(geometry.id);
            if (element && element.parentNode) {
                if (element.geometry) {
                    element.geometry.destroy();
                    element.geometry = null;
                }
                element.parentNode.removeChild(element);
                if (this.indexer) {
                    this.indexer.remove(element);
                }
                if (element._style.backgroundGraphic) {
                    var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX;
                    var bElem = OpenLayers.Util.getElement(backgroundId);
                    if (bElem && bElem.parentNode) {
                        bElem.parentNode.removeChild(bElem);
                    }
                }
            }
        }
    },
    nodeFactory: function (id, type) {
        var node = OpenLayers.Util.getElement(id);
        if (node) {
            if (!this.nodeTypeCompare(node, type)) {
                node.parentNode.removeChild(node);
                node = this.nodeFactory(id, type);
            }
        } else {
            node = this.createNode(type, id);
        }
        return node;
    },
    nodeTypeCompare: function (node, type) {},
    createNode: function (type, id) {},
    moveRoot: function (renderer) {
        var root = this.root;
        if (renderer.root.parentNode == this.rendererRoot) {
            root = renderer.root;
        }
        root.parentNode.removeChild(root);
        renderer.rendererRoot.appendChild(root);
    },
    getRenderLayerId: function () {
        return this.root.parentNode.parentNode.id;
    },
    isComplexSymbol: function (graphicName) {
        return (graphicName != "circle") && !! graphicName;
    },
    CLASS_NAME: "OpenLayers.Renderer.Elements"
});
OpenLayers.Renderer.symbol = {
    "star": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75],
    "cross": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0],
    "x": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0],
    "square": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
    "triangle": [0, 10, 10, 10, 5, 0, 0, 10]
};
OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, {
    center: null,
    zoom: null,
    layers: null,
    displayProjection: null,
    getParameters: function (url) {
        url = url || window.location.href;
        var parameters = OpenLayers.Util.getParameters(url);
        var index = url.indexOf('#');
        if (index > 0) {
            url = '?' + url.substring(index + 1, url.length);
            OpenLayers.Util.extend(parameters, OpenLayers.Util.getParameters(url));
        }
        return parameters;
    },
    setMap: function (map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
        for (var i = 0, len = this.map.controls.length; i < len; i++) {
            var control = this.map.controls[i];
            if ((control != this) && (control.CLASS_NAME == "OpenLayers.Control.ArgParser")) {
                if (control.displayProjection != this.displayProjection) {
                    this.displayProjection = control.displayProjection;
                }
                break;
            }
        }
        if (i == this.map.controls.length) {
            var args = this.getParameters();
            if (args.layers) {
                this.layers = args.layers;
                this.map.events.register('addlayer', this, this.configureLayers);
                this.configureLayers();
            }
            if (args.lat && args.lon) {
                this.center = new OpenLayers.LonLat(parseFloat(args.lon), parseFloat(args.lat));
                if (args.zoom) {
                    this.zoom = parseInt(args.zoom);
                }
                this.map.events.register('changebaselayer', this, this.setCenter);
                this.setCenter();
            }
        }
    },
    setCenter: function () {
        if (this.map.baseLayer) {
            this.map.events.unregister('changebaselayer', this, this.setCenter);
            if (this.displayProjection) {
                this.center.transform(this.displayProjection, this.map.getProjectionObject());
            }
            this.map.setCenter(this.center, this.zoom);
        }
    },
    configureLayers: function () {
        if (this.layers.length == this.map.layers.length) {
            this.map.events.unregister('addlayer', this, this.configureLayers);
            for (var i = 0, len = this.layers.length; i < len; i++) {
                var layer = this.map.layers[i];
                var c = this.layers.charAt(i);
                if (c == "B") {
                    this.map.setBaseLayer(layer);
                } else if ((c == "T") || (c == "F")) {
                    layer.setVisibility(c == "T");
                }
            }
        }
    },
    CLASS_NAME: "OpenLayers.Control.ArgParser"
});
OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, {
    argParserClass: OpenLayers.Control.ArgParser,
    element: null,
    anchor: false,
    base: '',
    displayProjection: null,
    initialize: function (element, base, options) {
        if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) {
            options = element;
            this.base = document.location.href;
            OpenLayers.Control.prototype.initialize.apply(this, [options]);
            if (this.element != null) {
                this.element = OpenLayers.Util.getElement(this.element);
            }
        } else {
            OpenLayers.Control.prototype.initialize.apply(this, [options]);
            this.element = OpenLayers.Util.getElement(element);
            this.base = base || document.location.href;
        }
    },
    destroy: function () {
        if (this.element.parentNode == this.div) {
            this.div.removeChild(this.element);
        }
        this.element = null;
        this.map.events.unregister('moveend', this, this.updateLink);
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    setMap: function (map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
        for (var i = 0, len = this.map.controls.length; i < len; i++) {
            var control = this.map.controls[i];
            if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) {
                if (control.displayProjection != this.displayProjection) {
                    this.displayProjection = control.displayProjection;
                }
                break;
            }
        }
        if (i == this.map.controls.length) {
            this.map.addControl(new this.argParserClass({
                'displayProjection': this.displayProjection
            }));
        }
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!this.element && !this.anchor) {
            this.element = document.createElement("a");
            this.element.innerHTML = OpenLayers.i18n("Permalink");
            this.element.href = "";
            this.div.appendChild(this.element);
        }
        this.map.events.on({
            'moveend': this.updateLink,
            'changelayer': this.updateLink,
            'changebaselayer': this.updateLink,
            scope: this
        });
        this.updateLink();
        return this.div;
    },
    updateLink: function () {
        var separator = this.anchor ? '#' : '?';
        var href = this.base;
        if (href.indexOf(separator) != -1) {
            href = href.substring(0, href.indexOf(separator));
        }
        href += separator + OpenLayers.Util.getParameterString(this.createParams());
        if (this.anchor && !this.element) {
            window.location.href = href;
        } else {
            this.element.href = href;
        }
    },
    createParams: function (center, zoom, layers) {
        center = center || this.map.getCenter();
        var params = OpenLayers.Util.getParameters(this.base);
        if (center) {
            params.zoom = zoom || this.map.getZoom();
            var lat = center.lat;
            var lon = center.lon;
            if (this.displayProjection) {
                var mapPosition = OpenLayers.Projection.transform({
                    x: lon,
                    y: lat
                }, this.map.getProjectionObject(), this.displayProjection);
                lon = mapPosition.x;
                lat = mapPosition.y;
            }
            params.lat = Math.round(lat * 100000) / 100000;
            params.lon = Math.round(lon * 100000) / 100000;
            layers = layers || this.map.layers;
            params.layers = '';
            for (var i = 0, len = layers.length; i < len; i++) {
                var layer = layers[i];
                if (layer.isBaseLayer) {
                    params.layers += (layer == this.map.baseLayer) ? "B" : "0";
                } else {
                    params.layers += (layer.getVisibility()) ? "T" : "F";
                }
            }
        }
        return params;
    },
    CLASS_NAME: "OpenLayers.Control.Permalink"
});
OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
    serviceVersion: "1.0.0",
    layername: null,
    type: null,
    isBaseLayer: true,
    tileOrigin: null,
    serverResolutions: null,
    zoomOffset: 0,
    initialize: function (name, url, options) {
        var newArguments = [];
        newArguments.push(name, url, {}, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
    },
    destroy: function () {
        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.TMS(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
        var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
        var z = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom() + this.zoomOffset;
        var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(path, url);
        }
        return url + path;
    },
    setMap: function (map) {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        if (!this.tileOrigin) {
            this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.bottom);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.TMS"
});
OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, {
    preload: false,
    activate: function () {
        if (OpenLayers.Strategy.prototype.activate.apply(this, arguments)) {
            this.layer.events.on({
                "refresh": this.load,
                scope: this
            });
            if (this.layer.visibility == true || this.preload) {
                this.load();
            } else {
                this.layer.events.on({
                    "visibilitychanged": this.load,
                    scope: this
                });
            }
            return true;
        }
        return false;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            this.layer.events.un({
                "refresh": this.load,
                "visibilitychanged": this.load,
                scope: this
            });
        }
        return deactivated;
    },
    load: function (options) {
        var layer = this.layer;
        layer.events.triggerEvent("loadstart");
        layer.protocol.read(OpenLayers.Util.applyDefaults({
            callback: OpenLayers.Function.bind(this.merge, this, layer.map.getProjectionObject()),
            filter: layer.filter
        }, options));
        layer.events.un({
            "visibilitychanged": this.load,
            scope: this
        });
    },
    merge: function (mapProjection, resp) {
        var layer = this.layer;
        layer.destroyFeatures();
        var features = resp.features;
        if (features && features.length > 0) {
            if (!mapProjection.equals(layer.projection)) {
                var geom;
                for (var i = 0, len = features.length; i < len; ++i) {
                    geom = features[i].geometry;
                    if (geom) {
                        geom.transform(layer.projection, mapProjection);
                    }
                }
            }
            layer.addFeatures(features);
        }
        layer.events.triggerEvent("loadend");
    },
    CLASS_NAME: "OpenLayers.Strategy.Fixed"
});
OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.1.0",
    errorProperty: "service",
    CLASS_NAME: "OpenLayers.Format.WFSCapabilities"
});
OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities, {
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        this.options = options;
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var capabilities = {};
        var root = data.documentElement;
        this.runChildNodes(capabilities, root);
        return capabilities;
    },
    runChildNodes: function (obj, node) {
        var children = node.childNodes;
        var childNode, processor;
        for (var i = 0; i < children.length; ++i) {
            childNode = children[i];
            if (childNode.nodeType == 1) {
                processor = this["read_cap_" + childNode.nodeName];
                if (processor) {
                    processor.apply(this, [obj, childNode]);
                }
            }
        }
    },
    read_cap_FeatureTypeList: function (request, node) {
        var featureTypeList = {
            featureTypes: []
        };
        this.runChildNodes(featureTypeList, node);
        request.featureTypeList = featureTypeList;
    },
    read_cap_FeatureType: function (featureTypeList, node, parentLayer) {
        var featureType = {};
        this.runChildNodes(featureType, node);
        featureTypeList.featureTypes.push(featureType);
    },
    read_cap_Name: function (obj, node) {
        var name = this.getChildValue(node);
        if (name) {
            var parts = name.split(":");
            obj.name = parts.pop();
            if (parts.length > 0) {
                obj.featureNS = this.lookupNamespaceURI(node, parts[0]);
            }
        }
    },
    read_cap_Title: function (obj, node) {
        var title = this.getChildValue(node);
        if (title) {
            obj.title = title;
        }
    },
    read_cap_Abstract: function (obj, node) {
        var abst = this.getChildValue(node);
        if (abst) {
            obj["abstract"] = abst;
        }
    },
    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1"
});
OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {
    initialize: function (options) {
        OpenLayers.Format.WFSCapabilities.v1.prototype.initialize.apply(this, [options]);
    },
    read_cap_DefaultSRS: function (obj, node) {
        var defaultSRS = this.getChildValue(node);
        if (defaultSRS) {
            obj.srs = defaultSRS;
        }
    },
    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0"
});
OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, {
    dataFrom: null,
    styleFrom: null,
    initialize: function (name, options) {
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, arguments);
    },
    addNodes: function (pointFeatures, options) {
        if (pointFeatures.length < 2) {
            OpenLayers.Console.error("At least two point features have to be added to create" + "a line from");
            return;
        }
        var lines = new Array(pointFeatures.length - 1);
        var pointFeature, startPoint, endPoint;
        for (var i = 0, len = pointFeatures.length; i < len; i++) {
            pointFeature = pointFeatures[i];
            endPoint = pointFeature.geometry;
            if (!endPoint) {
                var lonlat = pointFeature.lonlat;
                endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
            } else if (endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") {
                OpenLayers.Console.error("Only features with point geometries are supported.");
                return;
            }
            if (i > 0) {
                var attributes = (this.dataFrom != null) ? (pointFeatures[i + this.dataFrom].data || pointFeatures[i + this.dataFrom].attributes) : null;
                var style = (this.styleFrom != null) ? (pointFeatures[i + this.styleFrom].style) : null;
                var line = new OpenLayers.Geometry.LineString([startPoint, endPoint]);
                lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, style);
            }
            startPoint = endPoint;
        }
        this.addFeatures(lines, options);
    },
    CLASS_NAME: "OpenLayers.Layer.PointTrack"
});
OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;
OpenLayers.Layer.PointTrack.TARGET_NODE = 0;
OpenLayers.Layer.PointTrack.dataFrom = {
    'SOURCE_NODE': -1,
    'TARGET_NODE': 0
};
OpenLayers.Protocol.WFS = function (options) {
    options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS);
    var cls = OpenLayers.Protocol.WFS["v" + options.version.replace(/\./g, "_")];
    if (!cls) {
        throw "Unsupported WFS version: " + options.version;
    }
    return new cls(options);
};
OpenLayers.Protocol.WFS.fromWMSLayer = function (layer, options) {
    var typeName, featurePrefix;
    var param = layer.params["LAYERS"];
    var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":");
    if (parts.length > 1) {
        featurePrefix = parts[0];
    }
    typeName = parts.pop();
    var protocolOptions = {
        url: layer.url,
        featureType: typeName,
        featurePrefix: featurePrefix,
        srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(),
        version: "1.1.0"
    };
    return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions));
};
OpenLayers.Protocol.WFS.DEFAULTS = {
    "version": "1.0.0"
};
OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, {
    isBaseLayer: false,
    markers: null,
    drawn: false,
    initialize: function (name, options) {
        OpenLayers.Layer.prototype.initialize.apply(this, arguments);
        this.markers = [];
    },
    destroy: function () {
        this.clearMarkers();
        this.markers = null;
        OpenLayers.Layer.prototype.destroy.apply(this, arguments);
    },
    setOpacity: function (opacity) {
        if (opacity != this.opacity) {
            this.opacity = opacity;
            for (var i = 0, len = this.markers.length; i < len; i++) {
                this.markers[i].setOpacity(this.opacity);
            }
        }
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
        if (zoomChanged || !this.drawn) {
            for (var i = 0, len = this.markers.length; i < len; i++) {
                this.drawMarker(this.markers[i]);
            }
            this.drawn = true;
        }
    },
    addMarker: function (marker) {
        this.markers.push(marker);
        if (this.opacity != null) {
            marker.setOpacity(this.opacity);
        }
        if (this.map && this.map.getExtent()) {
            marker.map = this.map;
            this.drawMarker(marker);
        }
    },
    removeMarker: function (marker) {
        if (this.markers && this.markers.length) {
            OpenLayers.Util.removeItem(this.markers, marker);
            marker.erase();
        }
    },
    clearMarkers: function () {
        if (this.markers != null) {
            while (this.markers.length > 0) {
                this.removeMarker(this.markers[0]);
            }
        }
    },
    drawMarker: function (marker) {
        var px = this.map.getLayerPxFromLonLat(marker.lonlat);
        if (px == null) {
            marker.display(false);
        } else {
            if (!marker.isDrawn()) {
                var markerImg = marker.draw(px);
                this.div.appendChild(markerImg);
            } else if (marker.icon) {
                marker.icon.moveTo(px);
            }
        }
    },
    getDataExtent: function () {
        var maxExtent = null;
        if (this.markers && (this.markers.length > 0)) {
            var maxExtent = new OpenLayers.Bounds();
            for (var i = 0, len = this.markers.length; i < len; i++) {
                var marker = this.markers[i];
                maxExtent.extend(marker.lonlat);
            }
        }
        return maxExtent;
    },
    CLASS_NAME: "OpenLayers.Layer.Markers"
});
OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control, {
    slideFactor: 50,
    slideRatio: null,
    direction: null,
    type: OpenLayers.Control.TYPE_BUTTON,
    initialize: function (direction, options) {
        this.direction = direction;
        this.CLASS_NAME += this.direction;
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
    },
    trigger: function () {
        var getSlideFactor = OpenLayers.Function.bind(function (dim) {
            return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor;
        }, this);
        switch (this.direction) {
        case OpenLayers.Control.Pan.NORTH:
            this.map.pan(0, -getSlideFactor("h"));
            break;
        case OpenLayers.Control.Pan.SOUTH:
            this.map.pan(0, getSlideFactor("h"));
            break;
        case OpenLayers.Control.Pan.WEST:
            this.map.pan(-getSlideFactor("w"), 0);
            break;
        case OpenLayers.Control.Pan.EAST:
            this.map.pan(getSlideFactor("w"), 0);
            break;
        }
    },
    CLASS_NAME: "OpenLayers.Control.Pan"
});
OpenLayers.Control.Pan.NORTH = "North";
OpenLayers.Control.Pan.SOUTH = "South";
OpenLayers.Control.Pan.EAST = "East";
OpenLayers.Control.Pan.WEST = "West";
OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
    DEFAULT_PARAMS: {
        service: "WMS",
        version: "1.1.1",
        request: "GetMap",
        styles: "",
        format: "image/jpeg"
    },
    reproject: false,
    isBaseLayer: true,
    encodeBBOX: false,
    noMagic: false,
    yx: {
        'EPSG:4326': true
    },
    initialize: function (name, url, params, options) {
        var newArguments = [];
        params = OpenLayers.Util.upperCaseObject(params);
        if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) {
            params.EXCEPTIONS = "INIMAGE";
        }
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
        OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));
        if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") {
            if ((options == null) || (!options.isBaseLayer)) {
                this.isBaseLayer = false;
            }
            if (this.params.FORMAT == "image/jpeg") {
                this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png";
            }
        }
    },
    destroy: function () {
        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    reverseAxisOrder: function () {
        return (parseFloat(this.params.VERSION) >= 1.3 && !! this.yx[this.map.getProjectionObject().getCode()]);
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var imageSize = this.getImageSize();
        var newParams = {};
        var reverseAxisOrder = this.reverseAxisOrder();
        newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder);
        newParams.WIDTH = imageSize.w;
        newParams.HEIGHT = imageSize.h;
        var requestString = this.getFullRequestString(newParams);
        return requestString;
    },
    mergeNewParams: function (newParams) {
        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
        var newArguments = [upperParams];
        return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments);
    },
    getFullRequestString: function (newParams, altUrl) {
        var mapProjection = this.map.getProjectionObject();
        var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode();
        var value = (projectionCode == "none") ? null : projectionCode;
        if (parseFloat(this.params.VERSION) >= 1.3) {
            this.params.CRS = value;
        } else {
            this.params.SRS = value;
        }
        if (typeof this.params.TRANSPARENT == "boolean") {
            newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE";
        }
        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Layer.WMS"
});
OpenLayers.Layer.WMS.Untiled = OpenLayers.Class(OpenLayers.Layer.WMS, {
    singleTile: true,
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments);
        var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " + "will be removed in 3.0. Instead, you should use the " + "normal OpenLayers.Layer.WMS class, passing it the option " + "'singleTile' as true.";
        OpenLayers.Console.warn(msg);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WMS.Untiled(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.WMS.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.WMS.Untiled"
});
OpenLayers.Geometry.Surface = OpenLayers.Class(OpenLayers.Geometry, {
    initialize: function () {
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Geometry.Surface"
});
OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, {
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        var axl = new OpenLayers.Format.ArcXML();
        var parsed = axl.read(data);
        return parsed.features.feature;
    }
});
OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["beforesnap", "snap", "unsnap"],
    DEFAULTS: {
        tolerance: 10,
        node: true,
        edge: true,
        vertex: true
    },
    greedy: true,
    precedence: ["node", "vertex", "edge"],
    resolution: null,
    geoToleranceCache: null,
    layer: null,
    feature: null,
    point: null,
    initialize: function (options) {
        Array.prototype.push.apply(this.EVENT_TYPES, OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.options = options || {};
        if (this.options.layer) {
            this.setLayer(this.options.layer);
        }
        var defaults = OpenLayers.Util.extend({}, this.options.defaults);
        this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS);
        this.setTargets(this.options.targets);
        if (this.targets.length === 0 && this.layer) {
            this.addTargetLayer(this.layer);
        }
        this.geoToleranceCache = {};
    },
    setLayer: function (layer) {
        if (this.active) {
            this.deactivate();
            this.layer = layer;
            this.activate();
        } else {
            this.layer = layer;
        }
    },
    setTargets: function (targets) {
        this.targets = [];
        if (targets && targets.length) {
            var target;
            for (var i = 0, len = targets.length; i < len; ++i) {
                target = targets[i];
                if (target instanceof OpenLayers.Layer.Vector) {
                    this.addTargetLayer(target);
                } else {
                    this.addTarget(target);
                }
            }
        }
    },
    addTargetLayer: function (layer) {
        this.addTarget({
            layer: layer
        });
    },
    addTarget: function (target) {
        target = OpenLayers.Util.applyDefaults(target, this.defaults);
        target.nodeTolerance = target.nodeTolerance || target.tolerance;
        target.vertexTolerance = target.vertexTolerance || target.tolerance;
        target.edgeTolerance = target.edgeTolerance || target.tolerance;
        this.targets.push(target);
    },
    removeTargetLayer: function (layer) {
        var target;
        for (var i = this.targets.length - 1; i >= 0; --i) {
            target = this.targets[i];
            if (target.layer === layer) {
                this.removeTarget(target);
            }
        }
    },
    removeTarget: function (target) {
        return OpenLayers.Util.removeItem(this.targets, target);
    },
    activate: function () {
        var activated = OpenLayers.Control.prototype.activate.call(this);
        if (activated) {
            if (this.layer && this.layer.events) {
                this.layer.events.on({
                    sketchstarted: this.onSketchModified,
                    sketchmodified: this.onSketchModified,
                    vertexmodified: this.onVertexModified,
                    scope: this
                });
            }
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
        if (deactivated) {
            if (this.layer && this.layer.events) {
                this.layer.events.un({
                    sketchstarted: this.onSketchModified,
                    sketchmodified: this.onSketchModified,
                    vertexmodified: this.onVertexModified,
                    scope: this
                });
            }
        }
        this.feature = null;
        this.point = null;
        return deactivated;
    },
    onSketchModified: function (event) {
        this.feature = event.feature;
        this.considerSnapping(event.vertex, event.vertex);
    },
    onVertexModified: function (event) {
        this.feature = event.feature;
        var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel);
        this.considerSnapping(event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat));
    },
    considerSnapping: function (point, loc) {
        var best = {
            rank: Number.POSITIVE_INFINITY,
            dist: Number.POSITIVE_INFINITY,
            x: null,
            y: null
        };
        var snapped = false;
        var result, target;
        for (var i = 0, len = this.targets.length; i < len; ++i) {
            target = this.targets[i];
            result = this.testTarget(target, loc);
            if (result) {
                if (this.greedy) {
                    best = result;
                    best.target = target;
                    snapped = true;
                    break;
                } else {
                    if ((result.rank < best.rank) || (result.rank === best.rank && result.dist < best.dist)) {
                        best = result;
                        best.target = target;
                        snapped = true;
                    }
                }
            }
        }
        if (snapped) {
            var proceed = this.events.triggerEvent("beforesnap", {
                point: point,
                x: best.x,
                y: best.y,
                distance: best.dist,
                layer: best.target.layer,
                snapType: this.precedence[best.rank]
            });
            if (proceed !== false) {
                point.x = best.x;
                point.y = best.y;
                this.point = point;
                this.events.triggerEvent("snap", {
                    point: point,
                    snapType: this.precedence[best.rank],
                    layer: best.target.layer,
                    distance: best.dist
                });
            } else {
                snapped = false;
            }
        }
        if (this.point && !snapped) {
            point.x = loc.x;
            point.y = loc.y;
            this.point = null;
            this.events.triggerEvent("unsnap", {
                point: point
            });
        }
    },
    testTarget: function (target, loc) {
        var resolution = this.layer.map.getResolution();
        if ("minResolution" in target) {
            if (resolution < target.minResolution) {
                return null;
            }
        }
        if ("maxResolution" in target) {
            if (resolution >= target.maxResolution) {
                return null;
            }
        }
        var tolerance = {
            node: this.getGeoTolerance(target.nodeTolerance, resolution),
            vertex: this.getGeoTolerance(target.vertexTolerance, resolution),
            edge: this.getGeoTolerance(target.edgeTolerance, resolution)
        };
        var maxTolerance = Math.max(tolerance.node, tolerance.vertex, tolerance.edge);
        var result = {
            rank: Number.POSITIVE_INFINITY,
            dist: Number.POSITIVE_INFINITY
        };
        var eligible = false;
        var features = target.layer.features;
        var feature, type, vertices, vertex, closest, dist, found;
        var numTypes = this.precedence.length;
        var ll = new OpenLayers.LonLat(loc.x, loc.y);
        for (var i = 0, len = features.length; i < len; ++i) {
            feature = features[i];
            if (feature !== this.feature && !feature._sketch && feature.state !== OpenLayers.State.DELETE && (!target.filter || target.filter.evaluate(feature.attributes))) {
                if (feature.atPoint(ll, maxTolerance, maxTolerance)) {
                    for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) {
                        type = this.precedence[j];
                        if (target[type]) {
                            if (type === "edge") {
                                closest = feature.geometry.distanceTo(loc, {
                                    details: true
                                });
                                dist = closest.distance;
                                if (dist <= tolerance[type] && dist < result.dist) {
                                    result = {
                                        rank: j,
                                        dist: dist,
                                        x: closest.x0,
                                        y: closest.y0
                                    };
                                    eligible = true;
                                    break;
                                }
                            } else {
                                vertices = feature.geometry.getVertices(type === "node");
                                found = false;
                                for (var k = 0, klen = vertices.length; k < klen; ++k) {
                                    vertex = vertices[k];
                                    dist = vertex.distanceTo(loc);
                                    if (dist <= tolerance[type] && (j < result.rank || (j === result.rank && dist < result.dist))) {
                                        result = {
                                            rank: j,
                                            dist: dist,
                                            x: vertex.x,
                                            y: vertex.y
                                        };
                                        eligible = true;
                                        found = true;
                                    }
                                }
                                if (found) {
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return eligible ? result : null;
    },
    getGeoTolerance: function (tolerance, resolution) {
        if (resolution !== this.resolution) {
            this.resolution = resolution;
            this.geoToleranceCache = {};
        }
        var geoTolerance = this.geoToleranceCache[tolerance];
        if (geoTolerance === undefined) {
            geoTolerance = tolerance * resolution;
            this.geoToleranceCache[tolerance] = geoTolerance;
        }
        return geoTolerance;
    },
    destroy: function () {
        if (this.active) {
            this.deactivate();
        }
        delete this.layer;
        delete this.targets;
        OpenLayers.Control.prototype.destroy.call(this);
    },
    CLASS_NAME: "OpenLayers.Control.Snapping"
});
OpenLayers.Date = {
    toISOString: (function () {
        if ("toISOString" in Date.prototype) {
            return function (date) {
                return date.toISOString();
            };
        } else {
            function pad(num, len) {
                var str = num + "";
                while (str.length < len) {
                    str = "0" + str;
                }
                return str;
            }
            return function (date) {
                var str;
                if (isNaN(date.getTime())) {
                    str = "Invalid Date";
                } else {
                    str = date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1, 2) + "-" + pad(date.getUTCDate(), 2) + "T" + pad(date.getUTCHours(), 2) + ":" + pad(date.getUTCMinutes(), 2) + ":" + pad(date.getUTCSeconds(), 2) + "." + pad(date.getUTCMilliseconds(), 3) + "Z";
                }
                return str;
            };
        }
    })(),
    parse: function (str) {
        var date;
        var match = str.match(/^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))?$/);
        if (match && (match[1] || match[7])) {
            var year = parseInt(match[1], 10) || 0;
            var month = (parseInt(match[2], 10) - 1) || 0;
            var day = parseInt(match[3], 10) || 1;
            date = new Date(Date.UTC(year, month, day));
            var type = match[7];
            if (type) {
                var hours = parseInt(match[4], 10);
                var minutes = parseInt(match[5], 10);
                var secFrac = parseFloat(match[6]);
                var seconds = secFrac | 0;
                var milliseconds = Math.round(1000 * (secFrac - seconds));
                date.setUTCHours(hours, minutes, seconds, milliseconds);
                if (type !== "Z") {
                    var hoursOffset = parseInt(type, 10);
                    var minutesOffset = parseInt(match[8], 10) || 0;
                    var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
                    date = new Date(date.getTime() + offset);
                }
            }
        } else {
            date = new Date("invalid");
        }
        return date;
    }
};
(function () {
    var oXMLHttpRequest = window.XMLHttpRequest;
    var bGecko = !! window.controllers,
        bIE = window.document.all && !window.opera,
        bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/);

    function fXMLHttpRequest() {
        this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
        this._listeners = [];
    };

    function cXMLHttpRequest() {
        return new fXMLHttpRequest;
    };
    cXMLHttpRequest.prototype = fXMLHttpRequest.prototype;
    if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped;
    cXMLHttpRequest.UNSENT = 0;
    cXMLHttpRequest.OPENED = 1;
    cXMLHttpRequest.HEADERS_RECEIVED = 2;
    cXMLHttpRequest.LOADING = 3;
    cXMLHttpRequest.DONE = 4;
    cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT;
    cXMLHttpRequest.prototype.responseText = '';
    cXMLHttpRequest.prototype.responseXML = null;
    cXMLHttpRequest.prototype.status = 0;
    cXMLHttpRequest.prototype.statusText = '';
    cXMLHttpRequest.prototype.priority = "NORMAL";
    cXMLHttpRequest.prototype.onreadystatechange = null;
    cXMLHttpRequest.onreadystatechange = null;
    cXMLHttpRequest.onopen = null;
    cXMLHttpRequest.onsend = null;
    cXMLHttpRequest.onabort = null;
    cXMLHttpRequest.prototype.open = function (sMethod, sUrl, bAsync, sUser, sPassword) {
        delete this._headers;
        if (arguments.length < 3) bAsync = true;
        this._async = bAsync;
        var oRequest = this,
            nState = this.readyState,
            fOnUnload;
        if (bIE && bAsync) {
            fOnUnload = function () {
                if (nState != cXMLHttpRequest.DONE) {
                    fCleanTransport(oRequest);
                    oRequest.abort();
                }
            };
            window.attachEvent("onunload", fOnUnload);
        }
        if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments);
        if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword);
        else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser);
        else this._object.open(sMethod, sUrl, bAsync);
        this.readyState = cXMLHttpRequest.OPENED;
        fReadyStateChange(this);
        this._object.onreadystatechange = function () {
            if (bGecko && !bAsync) return;
            oRequest.readyState = oRequest._object.readyState;
            fSynchronizeValues(oRequest);
            if (oRequest._aborted) {
                oRequest.readyState = cXMLHttpRequest.UNSENT;
                return;
            }
            if (oRequest.readyState == cXMLHttpRequest.DONE) {
                delete oRequest._data;
                fCleanTransport(oRequest);
                if (bIE && bAsync) window.detachEvent("onunload", fOnUnload);
            }
            if (nState != oRequest.readyState) fReadyStateChange(oRequest);
            nState = oRequest.readyState;
        }
    };

    function fXMLHttpRequest_send(oRequest) {
        oRequest._object.send(oRequest._data);
        if (bGecko && !oRequest._async) {
            oRequest.readyState = cXMLHttpRequest.OPENED;
            fSynchronizeValues(oRequest);
            while (oRequest.readyState < cXMLHttpRequest.DONE) {
                oRequest.readyState++;
                fReadyStateChange(oRequest);
                if (oRequest._aborted) return;
            }
        }
    };
    cXMLHttpRequest.prototype.send = function (vData) {
        if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments);
        if (!arguments.length) vData = null;
        if (vData && vData.nodeType) {
            vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml;
            if (!oRequest._headers["Content-Type"]) oRequest._object.setRequestHeader("Content-Type", "application/xml");
        }
        this._data = vData;
        fXMLHttpRequest_send(this);
    };
    cXMLHttpRequest.prototype.abort = function () {
        if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments);
        if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true;
        this._object.abort();
        fCleanTransport(this);
        this.readyState = cXMLHttpRequest.UNSENT;
        delete this._data;
    };
    cXMLHttpRequest.prototype.getAllResponseHeaders = function () {
        return this._object.getAllResponseHeaders();
    };
    cXMLHttpRequest.prototype.getResponseHeader = function (sName) {
        return this._object.getResponseHeader(sName);
    };
    cXMLHttpRequest.prototype.setRequestHeader = function (sName, sValue) {
        if (!this._headers) this._headers = {};
        this._headers[sName] = sValue;
        return this._object.setRequestHeader(sName, sValue);
    };
    cXMLHttpRequest.prototype.addEventListener = function (sName, fHandler, bUseCapture) {
        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
        if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return;
        this._listeners.push([sName, fHandler, bUseCapture]);
    };
    cXMLHttpRequest.prototype.removeEventListener = function (sName, fHandler, bUseCapture) {
        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
        if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break;
        if (oListener) this._listeners.splice(nIndex, 1);
    };
    cXMLHttpRequest.prototype.dispatchEvent = function (oEvent) {
        var oEventPseudo = {
            'type': oEvent.type,
            'target': this,
            'currentTarget': this,
            'eventPhase': 2,
            'bubbles': oEvent.bubbles,
            'cancelable': oEvent.cancelable,
            'timeStamp': oEvent.timeStamp,
            'stopPropagation': function () {},
            'preventDefault': function () {},
            'initEvent': function () {}
        };
        if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]);
        for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++)
        if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]);
    };
    cXMLHttpRequest.prototype.toString = function () {
        return '[' + "object" + ' ' + "XMLHttpRequest" + ']';
    };
    cXMLHttpRequest.toString = function () {
        return '[' + "XMLHttpRequest" + ']';
    };

    function fReadyStateChange(oRequest) {
        if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest);
        oRequest.dispatchEvent({
            'type': "readystatechange",
            'bubbles': false,
            'cancelable': false,
            'timeStamp': new Date + 0
        });
    };

    function fGetDocument(oRequest) {
        var oDocument = oRequest.responseXML,
            sResponse = oRequest.responseText;
        if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) {
            oDocument = new window.ActiveXObject("Microsoft.XMLDOM");
            oDocument.async = false;
            oDocument.validateOnParse = false;
            oDocument.loadXML(sResponse);
        }
        if (oDocument) if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) return null;
        return oDocument;
    };

    function fSynchronizeValues(oRequest) {
        try {
            oRequest.responseText = oRequest._object.responseText;
        } catch (e) {}
        try {
            oRequest.responseXML = fGetDocument(oRequest._object);
        } catch (e) {}
        try {
            oRequest.status = oRequest._object.status;
        } catch (e) {}
        try {
            oRequest.statusText = oRequest._object.statusText;
        } catch (e) {}
    };

    function fCleanTransport(oRequest) {
        oRequest._object.onreadystatechange = new window.Function;
    };
    if (!window.Function.prototype.apply) {
        window.Function.prototype.apply = function (oRequest, oArguments) {
            if (!oArguments) oArguments = [];
            oRequest.__func = this;
            oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]);
            delete oRequest.__func;
        };
    };
    OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest;
})();
OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        kml: "http://www.opengis.net/kml/2.2",
        gx: "http://www.google.com/kml/ext/2.2"
    },
    kmlns: "http://earth.google.com/kml/2.0",
    placemarksDesc: "No description available",
    foldersName: "OpenLayers export",
    foldersDesc: "Exported on " + new Date(),
    extractAttributes: true,
    extractStyles: false,
    extractTracks: false,
    trackAttributes: null,
    internalns: null,
    features: null,
    styles: null,
    styleBaseUrl: "",
    fetched: null,
    maxDepth: 0,
    initialize: function (options) {
        this.regExes = {
            trimSpace: (/^\s*|\s*$/g),
            removeSpace: (/\s*/g),
            splitSpace: (/\s+/),
            trimComma: (/\s*,\s*/g),
            kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/),
            kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/),
            straightBracket: (/\$\[(.*?)\]/g)
        };
        this.externalProjection = new OpenLayers.Projection("EPSG:4326");
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        this.features = [];
        this.styles = {};
        this.fetched = {};
        var options = {
            depth: 0,
            styleBaseUrl: this.styleBaseUrl
        };
        return this.parseData(data, options);
    },
    parseData: function (data, options) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"];
        for (var i = 0, len = types.length; i < len; ++i) {
            var type = types[i];
            var nodes = this.getElementsByTagNameNS(data, "*", type);
            if (nodes.length == 0) {
                continue;
            }
            switch (type.toLowerCase()) {
            case "link":
            case "networklink":
                this.parseLinks(nodes, options);
                break;
            case "style":
                if (this.extractStyles) {
                    this.parseStyles(nodes, options);
                }
                break;
            case "stylemap":
                if (this.extractStyles) {
                    this.parseStyleMaps(nodes, options);
                }
                break;
            case "placemark":
                this.parseFeatures(nodes, options);
                break;
            }
        }
        return this.features;
    },
    parseLinks: function (nodes, options) {
        if (options.depth >= this.maxDepth) {
            return false;
        }
        var newOptions = OpenLayers.Util.extend({}, options);
        newOptions.depth++;
        for (var i = 0, len = nodes.length; i < len; i++) {
            var href = this.parseProperty(nodes[i], "*", "href");
            if (href && !this.fetched[href]) {
                this.fetched[href] = true;
                var data = this.fetchLink(href);
                if (data) {
                    this.parseData(data, newOptions);
                }
            }
        }
    },
    fetchLink: function (href) {
        var request = OpenLayers.Request.GET({
            url: href,
            async: false
        });
        if (request) {
            return request.responseText;
        }
    },
    parseStyles: function (nodes, options) {
        for (var i = 0, len = nodes.length; i < len; i++) {
            var style = this.parseStyle(nodes[i]);
            if (style) {
                var styleName = (options.styleBaseUrl || "") + "#" + style.id;
                this.styles[styleName] = style;
            }
        }
    },
    parseKmlColor: function (kmlColor) {
        var color = null;
        if (kmlColor) {
            var matches = kmlColor.match(this.regExes.kmlColor);
            if (matches) {
                color = {
                    color: '#' + matches[4] + matches[3] + matches[2],
                    opacity: parseInt(matches[1], 16) / 255
                };
            }
        }
        return color;
    },
    parseStyle: function (node) {
        var style = {};
        var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle", "LabelStyle"];
        var type, styleTypeNode, nodeList, geometry, parser;
        for (var i = 0, len = types.length; i < len; ++i) {
            type = types[i];
            styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0];
            if (!styleTypeNode) {
                continue;
            }
            switch (type.toLowerCase()) {
            case "linestyle":
                var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
                var color = this.parseKmlColor(kmlColor);
                if (color) {
                    style["strokeColor"] = color.color;
                    style["strokeOpacity"] = color.opacity;
                }
                var width = this.parseProperty(styleTypeNode, "*", "width");
                if (width) {
                    style["strokeWidth"] = width;
                }
                break;
            case "polystyle":
                var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
                var color = this.parseKmlColor(kmlColor);
                if (color) {
                    style["fillOpacity"] = color.opacity;
                    style["fillColor"] = color.color;
                }
                var fill = this.parseProperty(styleTypeNode, "*", "fill");
                if (fill == "0") {
                    style["fillColor"] = "none";
                }
                var outline = this.parseProperty(styleTypeNode, "*", "outline");
                if (outline == "0") {
                    style["strokeWidth"] = "0";
                }
                break;
            case "iconstyle":
                var scale = parseFloat(this.parseProperty(styleTypeNode, "*", "scale") || 1);
                var width = 32 * scale;
                var height = 32 * scale;
                var iconNode = this.getElementsByTagNameNS(styleTypeNode, "*", "Icon")[0];
                if (iconNode) {
                    var href = this.parseProperty(iconNode, "*", "href");
                    if (href) {
                        var w = this.parseProperty(iconNode, "*", "w");
                        var h = this.parseProperty(iconNode, "*", "h");
                        var google = "http://maps.google.com/mapfiles/kml";
                        if (OpenLayers.String.startsWith(href, google) && !w && !h) {
                            w = 64;
                            h = 64;
                            scale = scale / 2;
                        }
                        w = w || h;
                        h = h || w;
                        if (w) {
                            width = parseInt(w) * scale;
                        }
                        if (h) {
                            height = parseInt(h) * scale;
                        }
                        var matches = href.match(this.regExes.kmlIconPalette);
                        if (matches) {
                            var palette = matches[1];
                            var file_extension = matches[2];
                            var x = this.parseProperty(iconNode, "*", "x");
                            var y = this.parseProperty(iconNode, "*", "y");
                            var posX = x ? x / 32 : 0;
                            var posY = y ? (7 - y / 32) : 7;
                            var pos = posY * 8 + posX;
                            href = "http://maps.google.com/mapfiles/kml/pal" + palette + "/icon" + pos + file_extension;
                        }
                        style["graphicOpacity"] = 1;
                        style["externalGraphic"] = href;
                    }
                }
                var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, "*", "hotSpot")[0];
                if (hotSpotNode) {
                    var x = parseFloat(hotSpotNode.getAttribute("x"));
                    var y = parseFloat(hotSpotNode.getAttribute("y"));
                    var xUnits = hotSpotNode.getAttribute("xunits");
                    if (xUnits == "pixels") {
                        style["graphicXOffset"] = -x * scale;
                    } else if (xUnits == "insetPixels") {
                        style["graphicXOffset"] = -width + (x * scale);
                    } else if (xUnits == "fraction") {
                        style["graphicXOffset"] = -width * x;
                    }
                    var yUnits = hotSpotNode.getAttribute("yunits");
                    if (yUnits == "pixels") {
                        style["graphicYOffset"] = -height + (y * scale) + 1;
                    } else if (yUnits == "insetPixels") {
                        style["graphicYOffset"] = -(y * scale) + 1;
                    } else if (yUnits == "fraction") {
                        style["graphicYOffset"] = -height * (1 - y) + 1;
                    }
                }
                style["graphicWidth"] = width;
                style["graphicHeight"] = height;
                break;
            case "balloonstyle":
                var balloonStyle = OpenLayers.Util.getXmlNodeValue(styleTypeNode);
                if (balloonStyle) {
                    style["balloonStyle"] = balloonStyle.replace(this.regExes.straightBracket, "${$1}");
                }
                break;
            case "labelstyle":
                var kmlColor = this.parseProperty(styleTypeNode, "*", "color");
                var color = this.parseKmlColor(kmlColor);
                if (color) {
                    style["fontColor"] = color.color;
                    style["fontOpacity"] = color.opacity;
                }
                break;
            default:
            }
        }
        if (!style["strokeColor"] && style["fillColor"]) {
            style["strokeColor"] = style["fillColor"];
        }
        var id = node.getAttribute("id");
        if (id && style) {
            style.id = id;
        }
        return style;
    },
    parseStyleMaps: function (nodes, options) {
        for (var i = 0, len = nodes.length; i < len; i++) {
            var node = nodes[i];
            var pairs = this.getElementsByTagNameNS(node, "*", "Pair");
            var id = node.getAttribute("id");
            for (var j = 0, jlen = pairs.length; j < jlen; j++) {
                var pair = pairs[j];
                var key = this.parseProperty(pair, "*", "key");
                var styleUrl = this.parseProperty(pair, "*", "styleUrl");
                if (styleUrl && key == "normal") {
                    this.styles[(options.styleBaseUrl || "") + "#" + id] = this.styles[(options.styleBaseUrl || "") + styleUrl];
                }
                if (styleUrl && key == "highlight") {}
            }
        }
    },
    parseFeatures: function (nodes, options) {
        var features = [];
        for (var i = 0, len = nodes.length; i < len; i++) {
            var featureNode = nodes[i];
            var feature = this.parseFeature.apply(this, [featureNode]);
            if (feature) {
                if (this.extractStyles && feature.attributes && feature.attributes.styleUrl) {
                    feature.style = this.getStyle(feature.attributes.styleUrl, options);
                }
                if (this.extractStyles) {
                    var inlineStyleNode = this.getElementsByTagNameNS(featureNode, "*", "Style")[0];
                    if (inlineStyleNode) {
                        var inlineStyle = this.parseStyle(inlineStyleNode);
                        if (inlineStyle) {
                            feature.style = OpenLayers.Util.extend(feature.style, inlineStyle);
                        }
                    }
                }
                if (this.extractTracks) {
                    var tracks = this.getElementsByTagNameNS(featureNode, this.namespaces.gx, "Track");
                    if (tracks && tracks.length > 0) {
                        var track = tracks[0];
                        var container = {
                            features: [],
                            feature: feature
                        };
                        this.readNode(track, container);
                        if (container.features.length > 0) {
                            features.push.apply(features, container.features);
                        }
                    }
                } else {
                    features.push(feature);
                }
            } else {
                throw "Bad Placemark: " + i;
            }
        }
        this.features = this.features.concat(features);
    },
    readers: {
        "kml": {
            "when": function (node, container) {
                container.whens.push(OpenLayers.Date.parse(this.getChildValue(node)));
            },
            "_trackPointAttribute": function (node, container) {
                var name = node.nodeName.split(":").pop();
                container.attributes[name].push(this.getChildValue(node));
            }
        },
        "gx": {
            "Track": function (node, container) {
                var obj = {
                    whens: [],
                    points: [],
                    angles: []
                };
                if (this.trackAttributes) {
                    var name;
                    obj.attributes = {};
                    for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) {
                        name = this.trackAttributes[i];
                        obj.attributes[name] = [];
                        if (!(name in this.readers.kml)) {
                            this.readers.kml[name] = this.readers.kml._trackPointAttribute;
                        }
                    }
                }
                this.readChildNodes(node, obj);
                if (obj.whens.length !== obj.points.length) {
                    throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:coord (" + obj.points.length + ") elements.");
                }
                var hasAngles = obj.angles.length > 0;
                if (hasAngles && obj.whens.length !== obj.angles.length) {
                    throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:angles (" + obj.angles.length + ") elements.");
                }
                var feature, point, angles;
                for (var i = 0, ii = obj.whens.length; i < ii; ++i) {
                    feature = container.feature.clone();
                    feature.fid = container.feature.fid || container.feature.id;
                    point = obj.points[i];
                    feature.geometry = point;
                    if ("z" in point) {
                        feature.attributes.altitude = point.z;
                    }
                    if (this.internalProjection && this.externalProjection) {
                        feature.geometry.transform(this.externalProjection, this.internalProjection);
                    }
                    if (this.trackAttributes) {
                        for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) {
                            feature.attributes[name] = obj.attributes[this.trackAttributes[j]][i];
                        }
                    }
                    feature.attributes.when = obj.whens[i];
                    feature.attributes.trackId = container.feature.id;
                    if (hasAngles) {
                        angles = obj.angles[i];
                        feature.attributes.heading = parseFloat(angles[0]);
                        feature.attributes.tilt = parseFloat(angles[1]);
                        feature.attributes.roll = parseFloat(angles[2]);
                    }
                    container.features.push(feature);
                }
            },
            "coord": function (node, container) {
                var str = this.getChildValue(node);
                var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/);
                var point = new OpenLayers.Geometry.Point(coords[0], coords[1]);
                if (coords.length > 2) {
                    point.z = parseFloat(coords[2]);
                }
                container.points.push(point);
            },
            "angles": function (node, container) {
                var str = this.getChildValue(node);
                var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/);
                container.angles.push(parts);
            }
        }
    },
    parseFeature: function (node) {
        var order = ["MultiGeometry", "Polygon", "LineString", "Point"];
        var type, nodeList, geometry, parser;
        for (var i = 0, len = order.length; i < len; ++i) {
            type = order[i];
            this.internalns = node.namespaceURI ? node.namespaceURI : this.kmlns;
            nodeList = this.getElementsByTagNameNS(node, this.internalns, type);
            if (nodeList.length > 0) {
                var parser = this.parseGeometry[type.toLowerCase()];
                if (parser) {
                    geometry = parser.apply(this, [nodeList[0]]);
                    if (this.internalProjection && this.externalProjection) {
                        geometry.transform(this.externalProjection, this.internalProjection);
                    }
                } else {
                    OpenLayers.Console.error(OpenLayers.i18n("unsupportedGeometryType", {
                        'geomType': type
                    }));
                }
                break;
            }
        }
        var attributes;
        if (this.extractAttributes) {
            attributes = this.parseAttributes(node);
        }
        var feature = new OpenLayers.Feature.Vector(geometry, attributes);
        var fid = node.getAttribute("id") || node.getAttribute("name");
        if (fid != null) {
            feature.fid = fid;
        }
        return feature;
    },
    getStyle: function (styleUrl, options) {
        var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl);
        var newOptions = OpenLayers.Util.extend({}, options);
        newOptions.depth++;
        newOptions.styleBaseUrl = styleBaseUrl;
        if (!this.styles[styleUrl] && !OpenLayers.String.startsWith(styleUrl, "#") && newOptions.depth <= this.maxDepth && !this.fetched[styleBaseUrl]) {
            var data = this.fetchLink(styleBaseUrl);
            if (data) {
                this.parseData(data, newOptions);
            }
        }
        var style = OpenLayers.Util.extend({}, this.styles[styleUrl]);
        return style;
    },
    parseGeometry: {
        point: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.internalns, "coordinates");
            var coords = [];
            if (nodeList.length > 0) {
                var coordString = nodeList[0].firstChild.nodeValue;
                coordString = coordString.replace(this.regExes.removeSpace, "");
                coords = coordString.split(",");
            }
            var point = null;
            if (coords.length > 1) {
                if (coords.length == 2) {
                    coords[2] = null;
                }
                point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
            } else {
                throw "Bad coordinate string: " + coordString;
            }
            return point;
        },
        linestring: function (node, ring) {
            var nodeList = this.getElementsByTagNameNS(node, this.internalns, "coordinates");
            var line = null;
            if (nodeList.length > 0) {
                var coordString = this.getChildValue(nodeList[0]);
                coordString = coordString.replace(this.regExes.trimSpace, "");
                coordString = coordString.replace(this.regExes.trimComma, ",");
                var pointList = coordString.split(this.regExes.splitSpace);
                var numPoints = pointList.length;
                var points = new Array(numPoints);
                var coords, numCoords;
                for (var i = 0; i < numPoints; ++i) {
                    coords = pointList[i].split(",");
                    numCoords = coords.length;
                    if (numCoords > 1) {
                        if (coords.length == 2) {
                            coords[2] = null;
                        }
                        points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]);
                    } else {
                        throw "Bad LineString point coordinates: " + pointList[i];
                    }
                }
                if (numPoints) {
                    if (ring) {
                        line = new OpenLayers.Geometry.LinearRing(points);
                    } else {
                        line = new OpenLayers.Geometry.LineString(points);
                    }
                } else {
                    throw "Bad LineString coordinates: " + coordString;
                }
            }
            return line;
        },
        polygon: function (node) {
            var nodeList = this.getElementsByTagNameNS(node, this.internalns, "LinearRing");
            var numRings = nodeList.length;
            var components = new Array(numRings);
            if (numRings > 0) {
                var ring;
                for (var i = 0, len = nodeList.length; i < len; ++i) {
                    ring = this.parseGeometry.linestring.apply(this, [nodeList[i], true]);
                    if (ring) {
                        components[i] = ring;
                    } else {
                        throw "Bad LinearRing geometry: " + i;
                    }
                }
            }
            return new OpenLayers.Geometry.Polygon(components);
        },
        multigeometry: function (node) {
            var child, parser;
            var parts = [];
            var children = node.childNodes;
            for (var i = 0, len = children.length; i < len; ++i) {
                child = children[i];
                if (child.nodeType == 1) {
                    var type = (child.prefix) ? child.nodeName.split(":")[1] : child.nodeName;
                    var parser = this.parseGeometry[type.toLowerCase()];
                    if (parser) {
                        parts.push(parser.apply(this, [child]));
                    }
                }
            }
            return new OpenLayers.Geometry.Collection(parts);
        }
    },
    parseAttributes: function (node) {
        var attributes = {};
        var edNodes = node.getElementsByTagName("ExtendedData");
        if (edNodes.length) {
            attributes = this.parseExtendedData(edNodes[0]);
        }
        var child, grandchildren, grandchild;
        var children = node.childNodes;
        for (var i = 0, len = children.length; i < len; ++i) {
            child = children[i];
            if (child.nodeType == 1) {
                grandchildren = child.childNodes;
                if (grandchildren.length >= 1 && grandchildren.length <= 3) {
                    var grandchild;
                    switch (grandchildren.length) {
                    case 1:
                        grandchild = grandchildren[0];
                        break;
                    case 2:
                        var c1 = grandchildren[0];
                        var c2 = grandchildren[1];
                        grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ? c1 : c2;
                        break;
                    case 3:
                    default:
                        grandchild = grandchildren[1];
                        break;
                    }
                    if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {
                        var name = (child.prefix) ? child.nodeName.split(":")[1] : child.nodeName;
                        var value = OpenLayers.Util.getXmlNodeValue(grandchild);
                        if (value) {
                            value = value.replace(this.regExes.trimSpace, "");
                            attributes[name] = value;
                        }
                    }
                }
            }
        }
        return attributes;
    },
    parseExtendedData: function (node) {
        var attributes = {};
        var i, len, data, key;
        var dataNodes = node.getElementsByTagName("Data");
        for (i = 0, len = dataNodes.length; i < len; i++) {
            data = dataNodes[i];
            key = data.getAttribute("name");
            var ed = {};
            var valueNode = data.getElementsByTagName("value");
            if (valueNode.length) {
                ed['value'] = this.getChildValue(valueNode[0]);
            }
            var nameNode = data.getElementsByTagName("displayName");
            if (nameNode.length) {
                ed['displayName'] = this.getChildValue(nameNode[0]);
            }
            attributes[key] = ed;
        }
        var simpleDataNodes = node.getElementsByTagName("SimpleData");
        for (i = 0, len = simpleDataNodes.length; i < len; i++) {
            var ed = {};
            data = simpleDataNodes[i];
            key = data.getAttribute("name");
            ed['value'] = this.getChildValue(data);
            ed['displayName'] = key;
            attributes[key] = ed;
        }
        return attributes;
    },
    parseProperty: function (xmlNode, namespace, tagName) {
        var value;
        var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName);
        try {
            value = OpenLayers.Util.getXmlNodeValue(nodeList[0]);
        } catch (e) {
            value = null;
        }
        return value;
    },
    write: function (features) {
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        var kml = this.createElementNS(this.kmlns, "kml");
        var folder = this.createFolderXML();
        for (var i = 0, len = features.length; i < len; ++i) {
            folder.appendChild(this.createPlacemarkXML(features[i]));
        }
        kml.appendChild(folder);
        return OpenLayers.Format.XML.prototype.write.apply(this, [kml]);
    },
    createFolderXML: function () {
        var folder = this.createElementNS(this.kmlns, "Folder");
        if (this.foldersName) {
            var folderName = this.createElementNS(this.kmlns, "name");
            var folderNameText = this.createTextNode(this.foldersName);
            folderName.appendChild(folderNameText);
            folder.appendChild(folderName);
        }
        if (this.foldersDesc) {
            var folderDesc = this.createElementNS(this.kmlns, "description");
            var folderDescText = this.createTextNode(this.foldersDesc);
            folderDesc.appendChild(folderDescText);
            folder.appendChild(folderDesc);
        }
        return folder;
    },
    createPlacemarkXML: function (feature) {
        var placemarkName = this.createElementNS(this.kmlns, "name");
        var name = feature.style && feature.style.label ? feature.style.label : feature.attributes.name || feature.id;
        placemarkName.appendChild(this.createTextNode(name));
        var placemarkDesc = this.createElementNS(this.kmlns, "description");
        var desc = feature.attributes.description || this.placemarksDesc;
        placemarkDesc.appendChild(this.createTextNode(desc));
        var placemarkNode = this.createElementNS(this.kmlns, "Placemark");
        if (feature.fid != null) {
            placemarkNode.setAttribute("id", feature.fid);
        }
        placemarkNode.appendChild(placemarkName);
        placemarkNode.appendChild(placemarkDesc);
        var geometryNode = this.buildGeometryNode(feature.geometry);
        placemarkNode.appendChild(geometryNode);
        return placemarkNode;
    },
    buildGeometryNode: function (geometry) {
        var className = geometry.CLASS_NAME;
        var type = className.substring(className.lastIndexOf(".") + 1);
        var builder = this.buildGeometry[type.toLowerCase()];
        var node = null;
        if (builder) {
            node = builder.apply(this, [geometry]);
        }
        return node;
    },
    buildGeometry: {
        point: function (geometry) {
            var kml = this.createElementNS(this.kmlns, "Point");
            kml.appendChild(this.buildCoordinatesNode(geometry));
            return kml;
        },
        multipoint: function (geometry) {
            return this.buildGeometry.collection.apply(this, [geometry]);
        },
        linestring: function (geometry) {
            var kml = this.createElementNS(this.kmlns, "LineString");
            kml.appendChild(this.buildCoordinatesNode(geometry));
            return kml;
        },
        multilinestring: function (geometry) {
            return this.buildGeometry.collection.apply(this, [geometry]);
        },
        linearring: function (geometry) {
            var kml = this.createElementNS(this.kmlns, "LinearRing");
            kml.appendChild(this.buildCoordinatesNode(geometry));
            return kml;
        },
        polygon: function (geometry) {
            var kml = this.createElementNS(this.kmlns, "Polygon");
            var rings = geometry.components;
            var ringMember, ringGeom, type;
            for (var i = 0, len = rings.length; i < len; ++i) {
                type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs";
                ringMember = this.createElementNS(this.kmlns, type);
                ringGeom = this.buildGeometry.linearring.apply(this, [rings[i]]);
                ringMember.appendChild(ringGeom);
                kml.appendChild(ringMember);
            }
            return kml;
        },
        multipolygon: function (geometry) {
            return this.buildGeometry.collection.apply(this, [geometry]);
        },
        collection: function (geometry) {
            var kml = this.createElementNS(this.kmlns, "MultiGeometry");
            var child;
            for (var i = 0, len = geometry.components.length; i < len; ++i) {
                child = this.buildGeometryNode.apply(this, [geometry.components[i]]);
                if (child) {
                    kml.appendChild(child);
                }
            }
            return kml;
        }
    },
    buildCoordinatesNode: function (geometry) {
        var coordinatesNode = this.createElementNS(this.kmlns, "coordinates");
        var path;
        var points = geometry.components;
        if (points) {
            var point;
            var numPoints = points.length;
            var parts = new Array(numPoints);
            for (var i = 0; i < numPoints; ++i) {
                point = points[i];
                parts[i] = this.buildCoordinates(point);
            }
            path = parts.join(" ");
        } else {
            path = this.buildCoordinates(geometry);
        }
        var txtNode = this.createTextNode(path);
        coordinatesNode.appendChild(txtNode);
        return coordinatesNode;
    },
    buildCoordinates: function (point) {
        if (this.internalProjection && this.externalProjection) {
            point = point.clone();
            point.transform(this.internalProjection, this.externalProjection);
        }
        return point.x + "," + point.y;
    },
    CLASS_NAME: "OpenLayers.Format.KML"
});
OpenLayers.Popup = OpenLayers.Class({
    events: null,
    id: "",
    lonlat: null,
    div: null,
    contentSize: null,
    size: null,
    contentHTML: null,
    backgroundColor: "",
    opacity: "",
    border: "",
    contentDiv: null,
    groupDiv: null,
    closeDiv: null,
    autoSize: false,
    minSize: null,
    maxSize: null,
    displayClass: "olPopup",
    contentDisplayClass: "olPopupContent",
    padding: 0,
    disableFirefoxOverflowHack: false,
    fixPadding: function () {
        if (typeof this.padding == "number") {
            this.padding = new OpenLayers.Bounds(this.padding, this.padding, this.padding, this.padding);
        }
    },
    panMapIfOutOfView: false,
    keepInMap: false,
    closeOnMove: false,
    map: null,
    initialize: function (id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) {
        if (id == null) {
            id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
        }
        this.id = id;
        this.lonlat = lonlat;
        this.contentSize = (contentSize != null) ? contentSize : new OpenLayers.Size(OpenLayers.Popup.WIDTH, OpenLayers.Popup.HEIGHT);
        if (contentHTML != null) {
            this.contentHTML = contentHTML;
        }
        this.backgroundColor = OpenLayers.Popup.COLOR;
        this.opacity = OpenLayers.Popup.OPACITY;
        this.border = OpenLayers.Popup.BORDER;
        this.div = OpenLayers.Util.createDiv(this.id, null, null, null, null, null, "hidden");
        this.div.className = this.displayClass;
        var groupDivId = this.id + "_GroupDiv";
        this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, null, "relative", null, "hidden");
        var id = this.div.id + "_contentDiv";
        this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), null, "relative");
        this.contentDiv.className = this.contentDisplayClass;
        this.groupDiv.appendChild(this.contentDiv);
        this.div.appendChild(this.groupDiv);
        if (closeBox) {
            this.addCloseBox(closeBoxCallback);
        }
        this.registerEvents();
    },
    destroy: function () {
        this.id = null;
        this.lonlat = null;
        this.size = null;
        this.contentHTML = null;
        this.backgroundColor = null;
        this.opacity = null;
        this.border = null;
        if (this.closeOnMove && this.map) {
            this.map.events.unregister("movestart", this, this.hide);
        }
        this.events.destroy();
        this.events = null;
        if (this.closeDiv) {
            OpenLayers.Event.stopObservingElement(this.closeDiv);
            this.groupDiv.removeChild(this.closeDiv);
        }
        this.closeDiv = null;
        this.div.removeChild(this.groupDiv);
        this.groupDiv = null;
        if (this.map != null) {
            this.map.removePopup(this);
        }
        this.map = null;
        this.div = null;
        this.autoSize = null;
        this.minSize = null;
        this.maxSize = null;
        this.padding = null;
        this.panMapIfOutOfView = null;
    },
    draw: function (px) {
        if (px == null) {
            if ((this.lonlat != null) && (this.map != null)) {
                px = this.map.getLayerPxFromLonLat(this.lonlat);
            }
        }
        if (this.closeOnMove) {
            this.map.events.register("movestart", this, this.hide);
        }
        if (!this.disableFirefoxOverflowHack && OpenLayers.BROWSER_NAME == 'firefox') {
            this.map.events.register("movestart", this, function () {
                var style = document.defaultView.getComputedStyle(this.contentDiv, null);
                var currentOverflow = style.getPropertyValue("overflow");
                if (currentOverflow != "hidden") {
                    this.contentDiv._oldOverflow = currentOverflow;
                    this.contentDiv.style.overflow = "hidden";
                }
            });
            this.map.events.register("moveend", this, function () {
                var oldOverflow = this.contentDiv._oldOverflow;
                if (oldOverflow) {
                    this.contentDiv.style.overflow = oldOverflow;
                    this.contentDiv._oldOverflow = null;
                }
            });
        }
        this.moveTo(px);
        if (!this.autoSize && !this.size) {
            this.setSize(this.contentSize);
        }
        this.setBackgroundColor();
        this.setOpacity();
        this.setBorder();
        this.setContentHTML();
        if (this.panMapIfOutOfView) {
            this.panIntoView();
        }
        return this.div;
    },
    updatePosition: function () {
        if ((this.lonlat) && (this.map)) {
            var px = this.map.getLayerPxFromLonLat(this.lonlat);
            if (px) {
                this.moveTo(px);
            }
        }
    },
    moveTo: function (px) {
        if ((px != null) && (this.div != null)) {
            this.div.style.left = px.x + "px";
            this.div.style.top = px.y + "px";
        }
    },
    visible: function () {
        return OpenLayers.Element.visible(this.div);
    },
    toggle: function () {
        if (this.visible()) {
            this.hide();
        } else {
            this.show();
        }
    },
    show: function () {
        this.div.style.display = '';
        if (this.panMapIfOutOfView) {
            this.panIntoView();
        }
    },
    hide: function () {
        this.div.style.display = 'none';
    },
    setSize: function (contentSize) {
        this.size = contentSize.clone();
        var contentDivPadding = this.getContentDivPadding();
        var wPadding = contentDivPadding.left + contentDivPadding.right;
        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
        this.fixPadding();
        wPadding += this.padding.left + this.padding.right;
        hPadding += this.padding.top + this.padding.bottom;
        if (this.closeDiv) {
            var closeDivWidth = parseInt(this.closeDiv.style.width);
            wPadding += closeDivWidth + contentDivPadding.right;
        }
        this.size.w += wPadding;
        this.size.h += hPadding;
        if (OpenLayers.BROWSER_NAME == "msie") {
            this.contentSize.w += contentDivPadding.left + contentDivPadding.right;
            this.contentSize.h += contentDivPadding.bottom + contentDivPadding.top;
        }
        if (this.div != null) {
            this.div.style.width = this.size.w + "px";
            this.div.style.height = this.size.h + "px";
        }
        if (this.contentDiv != null) {
            this.contentDiv.style.width = contentSize.w + "px";
            this.contentDiv.style.height = contentSize.h + "px";
        }
    },
    updateSize: function () {
        var preparedHTML = "<div class='" + this.contentDisplayClass + "'>" + this.contentDiv.innerHTML + "</div>";
        var containerElement = (this.map) ? this.map.layerContainerDiv : document.body;
        var realSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, null, {
            displayClass: this.displayClass,
            containerElement: containerElement
        });
        var safeSize = this.getSafeContentSize(realSize);
        var newSize = null;
        if (safeSize.equals(realSize)) {
            newSize = realSize;
        } else {
            var fixedSize = new OpenLayers.Size();
            fixedSize.w = (safeSize.w < realSize.w) ? safeSize.w : null;
            fixedSize.h = (safeSize.h < realSize.h) ? safeSize.h : null;
            if (fixedSize.w && fixedSize.h) {
                newSize = safeSize;
            } else {
                var clippedSize = OpenLayers.Util.getRenderedDimensions(preparedHTML, fixedSize, {
                    displayClass: this.contentDisplayClass,
                    containerElement: containerElement
                });
                var currentOverflow = OpenLayers.Element.getStyle(this.contentDiv, "overflow");
                if ((currentOverflow != "hidden") && (clippedSize.equals(safeSize))) {
                    var scrollBar = OpenLayers.Util.getScrollbarWidth();
                    if (fixedSize.w) {
                        clippedSize.h += scrollBar;
                    } else {
                        clippedSize.w += scrollBar;
                    }
                }
                newSize = this.getSafeContentSize(clippedSize);
            }
        }
        this.setSize(newSize);
    },
    setBackgroundColor: function (color) {
        if (color != undefined) {
            this.backgroundColor = color;
        }
        if (this.div != null) {
            this.div.style.backgroundColor = this.backgroundColor;
        }
    },
    setOpacity: function (opacity) {
        if (opacity != undefined) {
            this.opacity = opacity;
        }
        if (this.div != null) {
            this.div.style.opacity = this.opacity;
            this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')';
        }
    },
    setBorder: function (border) {
        if (border != undefined) {
            this.border = border;
        }
        if (this.div != null) {
            this.div.style.border = this.border;
        }
    },
    setContentHTML: function (contentHTML) {
        if (contentHTML != null) {
            this.contentHTML = contentHTML;
        }
        if ((this.contentDiv != null) && (this.contentHTML != null) && (this.contentHTML != this.contentDiv.innerHTML)) {
            this.contentDiv.innerHTML = this.contentHTML;
            if (this.autoSize) {
                this.registerImageListeners();
                this.updateSize();
            }
        }
    },
    registerImageListeners: function () {
        var onImgLoad = function () {
                this.popup.updateSize();
                if (this.popup.visible() && this.popup.panMapIfOutOfView) {
                    this.popup.panIntoView();
                }
                OpenLayers.Event.stopObserving(this.img, "load", this.img._onImageLoad);
            };
        var images = this.contentDiv.getElementsByTagName("img");
        for (var i = 0, len = images.length; i < len; i++) {
            var img = images[i];
            if (img.width == 0 || img.height == 0) {
                var context = {
                    'popup': this,
                    'img': img
                };
                img._onImgLoad = OpenLayers.Function.bind(onImgLoad, context);
                OpenLayers.Event.observe(img, 'load', img._onImgLoad);
            }
        }
    },
    getSafeContentSize: function (size) {
        var safeContentSize = size.clone();
        var contentDivPadding = this.getContentDivPadding();
        var wPadding = contentDivPadding.left + contentDivPadding.right;
        var hPadding = contentDivPadding.top + contentDivPadding.bottom;
        this.fixPadding();
        wPadding += this.padding.left + this.padding.right;
        hPadding += this.padding.top + this.padding.bottom;
        if (this.closeDiv) {
            var closeDivWidth = parseInt(this.closeDiv.style.width);
            wPadding += closeDivWidth + contentDivPadding.right;
        }
        if (this.minSize) {
            safeContentSize.w = Math.max(safeContentSize.w, (this.minSize.w - wPadding));
            safeContentSize.h = Math.max(safeContentSize.h, (this.minSize.h - hPadding));
        }
        if (this.maxSize) {
            safeContentSize.w = Math.min(safeContentSize.w, (this.maxSize.w - wPadding));
            safeContentSize.h = Math.min(safeContentSize.h, (this.maxSize.h - hPadding));
        }
        if (this.map && this.map.size) {
            var extraX = 0,
                extraY = 0;
            if (this.keepInMap && !this.panMapIfOutOfView) {
                var px = this.map.getPixelFromLonLat(this.lonlat);
                switch (this.relativePosition) {
                case "tr":
                    extraX = px.x;
                    extraY = this.map.size.h - px.y;
                    break;
                case "tl":
                    extraX = this.map.size.w - px.x;
                    extraY = this.map.size.h - px.y;
                    break;
                case "bl":
                    extraX = this.map.size.w - px.x;
                    extraY = px.y;
                    break;
                case "br":
                    extraX = px.x;
                    extraY = px.y;
                    break;
                default:
                    extraX = px.x;
                    extraY = this.map.size.h - px.y;
                    break;
                }
            }
            var maxY = this.map.size.h - this.map.paddingForPopups.top - this.map.paddingForPopups.bottom - hPadding - extraY;
            var maxX = this.map.size.w - this.map.paddingForPopups.left - this.map.paddingForPopups.right - wPadding - extraX;
            safeContentSize.w = Math.min(safeContentSize.w, maxX);
            safeContentSize.h = Math.min(safeContentSize.h, maxY);
        }
        return safeContentSize;
    },
    getContentDivPadding: function () {
        var contentDivPadding = this._contentDivPadding;
        if (!contentDivPadding) {
            if (this.div.parentNode == null) {
                this.div.style.display = "none";
                document.body.appendChild(this.div);
            }
            contentDivPadding = new OpenLayers.Bounds(OpenLayers.Element.getStyle(this.contentDiv, "padding-left"), OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"), OpenLayers.Element.getStyle(this.contentDiv, "padding-right"), OpenLayers.Element.getStyle(this.contentDiv, "padding-top"));
            this._contentDivPadding = contentDivPadding;
            if (this.div.parentNode == document.body) {
                document.body.removeChild(this.div);
                this.div.style.display = "";
            }
        }
        return contentDivPadding;
    },
    addCloseBox: function (callback) {
        this.closeDiv = OpenLayers.Util.createDiv(this.id + "_close", null, new OpenLayers.Size(17, 17));
        this.closeDiv.className = "olPopupCloseBox";
        var contentDivPadding = this.getContentDivPadding();
        this.closeDiv.style.right = contentDivPadding.right + "px";
        this.closeDiv.style.top = contentDivPadding.top + "px";
        this.groupDiv.appendChild(this.closeDiv);
        var closePopup = callback ||
        function (e) {
            this.hide();
            OpenLayers.Event.stop(e);
        };
        OpenLayers.Event.observe(this.closeDiv, "touchend", OpenLayers.Function.bindAsEventListener(closePopup, this));
        OpenLayers.Event.observe(this.closeDiv, "click", OpenLayers.Function.bindAsEventListener(closePopup, this));
    },
    panIntoView: function () {
        var mapSize = this.map.getSize();
        var origTL = this.map.getViewPortPxFromLayerPx(new OpenLayers.Pixel(parseInt(this.div.style.left), parseInt(this.div.style.top)));
        var newTL = origTL.clone();
        if (origTL.x < this.map.paddingForPopups.left) {
            newTL.x = this.map.paddingForPopups.left;
        } else if ((origTL.x + this.size.w) > (mapSize.w - this.map.paddingForPopups.right)) {
            newTL.x = mapSize.w - this.map.paddingForPopups.right - this.size.w;
        }
        if (origTL.y < this.map.paddingForPopups.top) {
            newTL.y = this.map.paddingForPopups.top;
        } else if ((origTL.y + this.size.h) > (mapSize.h - this.map.paddingForPopups.bottom)) {
            newTL.y = mapSize.h - this.map.paddingForPopups.bottom - this.size.h;
        }
        var dx = origTL.x - newTL.x;
        var dy = origTL.y - newTL.y;
        this.map.pan(dx, dy);
    },
    registerEvents: function () {
        this.events = new OpenLayers.Events(this, this.div, null, true);

        function onTouchstart(evt) {
            OpenLayers.Event.stop(evt, true);
        }
        this.events.on({
            "mousedown": this.onmousedown,
            "mousemove": this.onmousemove,
            "mouseup": this.onmouseup,
            "click": this.onclick,
            "mouseout": this.onmouseout,
            "dblclick": this.ondblclick,
            "touchstart": onTouchstart,
            scope: this
        });
    },
    onmousedown: function (evt) {
        this.mousedown = true;
        OpenLayers.Event.stop(evt, true);
    },
    onmousemove: function (evt) {
        if (this.mousedown) {
            OpenLayers.Event.stop(evt, true);
        }
    },
    onmouseup: function (evt) {
        if (this.mousedown) {
            this.mousedown = false;
            OpenLayers.Event.stop(evt, true);
        }
    },
    onclick: function (evt) {
        OpenLayers.Event.stop(evt, true);
    },
    onmouseout: function (evt) {
        this.mousedown = false;
    },
    ondblclick: function (evt) {
        OpenLayers.Event.stop(evt, true);
    },
    CLASS_NAME: "OpenLayers.Popup"
});
OpenLayers.Popup.WIDTH = 200;
OpenLayers.Popup.HEIGHT = 200;
OpenLayers.Popup.COLOR = "white";
OpenLayers.Popup.OPACITY = 1;
OpenLayers.Popup.BORDER = "0px";
OpenLayers.Popup.Anchored = OpenLayers.Class(OpenLayers.Popup, {
    relativePosition: null,
    keepInMap: true,
    anchor: null,
    initialize: function (id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {
        var newArguments = [id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback];
        OpenLayers.Popup.prototype.initialize.apply(this, newArguments);
        this.anchor = (anchor != null) ? anchor : {
            size: new OpenLayers.Size(0, 0),
            offset: new OpenLayers.Pixel(0, 0)
        };
    },
    destroy: function () {
        this.anchor = null;
        this.relativePosition = null;
        OpenLayers.Popup.prototype.destroy.apply(this, arguments);
    },
    show: function () {
        this.updatePosition();
        OpenLayers.Popup.prototype.show.apply(this, arguments);
    },
    moveTo: function (px) {
        var oldRelativePosition = this.relativePosition;
        this.relativePosition = this.calculateRelativePosition(px);
        var newPx = this.calculateNewPx(px);
        var newArguments = new Array(newPx);
        OpenLayers.Popup.prototype.moveTo.apply(this, newArguments);
        if (this.relativePosition != oldRelativePosition) {
            this.updateRelativePosition();
        }
    },
    setSize: function (contentSize) {
        OpenLayers.Popup.prototype.setSize.apply(this, arguments);
        if ((this.lonlat) && (this.map)) {
            var px = this.map.getLayerPxFromLonLat(this.lonlat);
            this.moveTo(px);
        }
    },
    calculateRelativePosition: function (px) {
        var lonlat = this.map.getLonLatFromLayerPx(px);
        var extent = this.map.getExtent();
        var quadrant = extent.determineQuadrant(lonlat);
        return OpenLayers.Bounds.oppositeQuadrant(quadrant);
    },
    updateRelativePosition: function () {},
    calculateNewPx: function (px) {
        var newPx = px.offset(this.anchor.offset);
        var size = this.size || this.contentSize;
        var top = (this.relativePosition.charAt(0) == 't');
        newPx.y += (top) ? -size.h : this.anchor.size.h;
        var left = (this.relativePosition.charAt(1) == 'l');
        newPx.x += (left) ? -size.w : this.anchor.size.w;
        return newPx;
    },
    CLASS_NAME: "OpenLayers.Popup.Anchored"
});
OpenLayers.Rico = OpenLayers.Rico || {};
OpenLayers.Rico.Color = OpenLayers.Class({
    initialize: function (red, green, blue) {
        this.rgb = {
            r: red,
            g: green,
            b: blue
        };
    },
    setRed: function (r) {
        this.rgb.r = r;
    },
    setGreen: function (g) {
        this.rgb.g = g;
    },
    setBlue: function (b) {
        this.rgb.b = b;
    },
    setHue: function (h) {
        var hsb = this.asHSB();
        hsb.h = h;
        this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
    },
    setSaturation: function (s) {
        var hsb = this.asHSB();
        hsb.s = s;
        this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
    },
    setBrightness: function (b) {
        var hsb = this.asHSB();
        hsb.b = b;
        this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
    },
    darken: function (percent) {
        var hsb = this.asHSB();
        this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent, 0));
    },
    brighten: function (percent) {
        var hsb = this.asHSB();
        this.rgb = OpenLayers.Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent, 1));
    },
    blend: function (other) {
        this.rgb.r = Math.floor((this.rgb.r + other.rgb.r) / 2);
        this.rgb.g = Math.floor((this.rgb.g + other.rgb.g) / 2);
        this.rgb.b = Math.floor((this.rgb.b + other.rgb.b) / 2);
    },
    isBright: function () {
        var hsb = this.asHSB();
        return this.asHSB().b > 0.5;
    },
    isDark: function () {
        return !this.isBright();
    },
    asRGB: function () {
        return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
    },
    asHex: function () {
        return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
    },
    asHSB: function () {
        return OpenLayers.Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
    },
    toString: function () {
        return this.asHex();
    }
});
OpenLayers.Rico.Color.createFromHex = function (hexCode) {
    if (hexCode.length == 4) {
        var shortHexCode = hexCode;
        var hexCode = '#';
        for (var i = 1; i < 4; i++) {
            hexCode += (shortHexCode.charAt(i) + shortHexCode.charAt(i));
        }
    }
    if (hexCode.indexOf('#') == 0) {
        hexCode = hexCode.substring(1);
    }
    var red = hexCode.substring(0, 2);
    var green = hexCode.substring(2, 4);
    var blue = hexCode.substring(4, 6);
    return new OpenLayers.Rico.Color(parseInt(red, 16), parseInt(green, 16), parseInt(blue, 16));
};
OpenLayers.Rico.Color.createColorFromBackground = function (elem) {
    var actualColor = OpenLayers.Element.getStyle(OpenLayers.Util.getElement(elem), "backgroundColor");
    if (actualColor == "transparent" && elem.parentNode) {
        return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);
    }
    if (actualColor == null) {
        return new OpenLayers.Rico.Color(255, 255, 255);
    }
    if (actualColor.indexOf("rgb(") == 0) {
        var colors = actualColor.substring(4, actualColor.length - 1);
        var colorArray = colors.split(",");
        return new OpenLayers.Rico.Color(parseInt(colorArray[0]), parseInt(colorArray[1]), parseInt(colorArray[2]));
    } else if (actualColor.indexOf("#") == 0) {
        return OpenLayers.Rico.Color.createFromHex(actualColor);
    } else {
        return new OpenLayers.Rico.Color(255, 255, 255);
    }
};
OpenLayers.Rico.Color.HSBtoRGB = function (hue, saturation, brightness) {
    var red = 0;
    var green = 0;
    var blue = 0;
    if (saturation == 0) {
        red = parseInt(brightness * 255.0 + 0.5);
        green = red;
        blue = red;
    } else {
        var h = (hue - Math.floor(hue)) * 6.0;
        var f = h - Math.floor(h);
        var p = brightness * (1.0 - saturation);
        var q = brightness * (1.0 - saturation * f);
        var t = brightness * (1.0 - (saturation * (1.0 - f)));
        switch (parseInt(h)) {
        case 0:
            red = (brightness * 255.0 + 0.5);
            green = (t * 255.0 + 0.5);
            blue = (p * 255.0 + 0.5);
            break;
        case 1:
            red = (q * 255.0 + 0.5);
            green = (brightness * 255.0 + 0.5);
            blue = (p * 255.0 + 0.5);
            break;
        case 2:
            red = (p * 255.0 + 0.5);
            green = (brightness * 255.0 + 0.5);
            blue = (t * 255.0 + 0.5);
            break;
        case 3:
            red = (p * 255.0 + 0.5);
            green = (q * 255.0 + 0.5);
            blue = (brightness * 255.0 + 0.5);
            break;
        case 4:
            red = (t * 255.0 + 0.5);
            green = (p * 255.0 + 0.5);
            blue = (brightness * 255.0 + 0.5);
            break;
        case 5:
            red = (brightness * 255.0 + 0.5);
            green = (p * 255.0 + 0.5);
            blue = (q * 255.0 + 0.5);
            break;
        }
    }
    return {
        r: parseInt(red),
        g: parseInt(green),
        b: parseInt(blue)
    };
};
OpenLayers.Rico.Color.RGBtoHSB = function (r, g, b) {
    var hue;
    var saturation;
    var brightness;
    var cmax = (r > g) ? r : g;
    if (b > cmax) {
        cmax = b;
    }
    var cmin = (r < g) ? r : g;
    if (b < cmin) {
        cmin = b;
    }
    brightness = cmax / 255.0;
    if (cmax != 0) {
        saturation = (cmax - cmin) / cmax;
    } else {
        saturation = 0;
    }
    if (saturation == 0) {
        hue = 0;
    } else {
        var redc = (cmax - r) / (cmax - cmin);
        var greenc = (cmax - g) / (cmax - cmin);
        var bluec = (cmax - b) / (cmax - cmin);
        if (r == cmax) {
            hue = bluec - greenc;
        } else if (g == cmax) {
            hue = 2.0 + redc - bluec;
        } else {
            hue = 4.0 + greenc - redc;
        }
        hue = hue / 6.0;
        if (hue < 0) {
            hue = hue + 1.0;
        }
    }
    return {
        h: hue,
        s: saturation,
        b: brightness
    };
};
OpenLayers.Rico = OpenLayers.Rico || {};
OpenLayers.Rico.Corner = {
    round: function (e, options) {
        e = OpenLayers.Util.getElement(e);
        this._setOptions(options);
        var color = this.options.color;
        if (this.options.color == "fromElement") {
            color = this._background(e);
        }
        var bgColor = this.options.bgColor;
        if (this.options.bgColor == "fromParent") {
            bgColor = this._background(e.offsetParent);
        }
        this._roundCornersImpl(e, color, bgColor);
    },
    changeColor: function (theDiv, newColor) {
        theDiv.style.backgroundColor = newColor;
        var spanElements = theDiv.parentNode.getElementsByTagName("span");
        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
            spanElements[currIdx].style.backgroundColor = newColor;
        }
    },
    changeOpacity: function (theDiv, newOpacity) {
        var mozillaOpacity = newOpacity;
        var ieOpacity = 'alpha(opacity=' + newOpacity * 100 + ')';
        theDiv.style.opacity = mozillaOpacity;
        theDiv.style.filter = ieOpacity;
        var spanElements = theDiv.parentNode.getElementsByTagName("span");
        for (var currIdx = 0; currIdx < spanElements.length; currIdx++) {
            spanElements[currIdx].style.opacity = mozillaOpacity;
            spanElements[currIdx].style.filter = ieOpacity;
        }
    },
    reRound: function (theDiv, options) {
        var topRico = theDiv.parentNode.childNodes[0];
        var bottomRico = theDiv.parentNode.childNodes[2];
        theDiv.parentNode.removeChild(topRico);
        theDiv.parentNode.removeChild(bottomRico);
        this.round(theDiv.parentNode, options);
    },
    _roundCornersImpl: function (e, color, bgColor) {
        if (this.options.border) {
            this._renderBorder(e, bgColor);
        }
        if (this._isTopRounded()) {
            this._roundTopCorners(e, color, bgColor);
        }
        if (this._isBottomRounded()) {
            this._roundBottomCorners(e, color, bgColor);
        }
    },
    _renderBorder: function (el, bgColor) {
        var borderValue = "1px solid " + this._borderColor(bgColor);
        var borderL = "border-left: " + borderValue;
        var borderR = "border-right: " + borderValue;
        var style = "style='" + borderL + ";" + borderR + "'";
        el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>";
    },
    _roundTopCorners: function (el, color, bgColor) {
        var corner = this._createCorner(bgColor);
        for (var i = 0; i < this.options.numSlices; i++) {
            corner.appendChild(this._createCornerSlice(color, bgColor, i, "top"));
        }
        el.style.paddingTop = 0;
        el.insertBefore(corner, el.firstChild);
    },
    _roundBottomCorners: function (el, color, bgColor) {
        var corner = this._createCorner(bgColor);
        for (var i = (this.options.numSlices - 1); i >= 0; i--) {
            corner.appendChild(this._createCornerSlice(color, bgColor, i, "bottom"));
        }
        el.style.paddingBottom = 0;
        el.appendChild(corner);
    },
    _createCorner: function (bgColor) {
        var corner = document.createElement("div");
        corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
        return corner;
    },
    _createCornerSlice: function (color, bgColor, n, position) {
        var slice = document.createElement("span");
        var inStyle = slice.style;
        inStyle.backgroundColor = color;
        inStyle.display = "block";
        inStyle.height = "1px";
        inStyle.overflow = "hidden";
        inStyle.fontSize = "1px";
        var borderColor = this._borderColor(color, bgColor);
        if (this.options.border && n == 0) {
            inStyle.borderTopStyle = "solid";
            inStyle.borderTopWidth = "1px";
            inStyle.borderLeftWidth = "0px";
            inStyle.borderRightWidth = "0px";
            inStyle.borderBottomWidth = "0px";
            inStyle.height = "0px";
            inStyle.borderColor = borderColor;
        } else if (borderColor) {
            inStyle.borderColor = borderColor;
            inStyle.borderStyle = "solid";
            inStyle.borderWidth = "0px 1px";
        }
        if (!this.options.compact && (n == (this.options.numSlices - 1))) {
            inStyle.height = "2px";
        }
        this._setMargin(slice, n, position);
        this._setBorder(slice, n, position);
        return slice;
    },
    _setOptions: function (options) {
        this.options = {
            corners: "all",
            color: "fromElement",
            bgColor: "fromParent",
            blend: true,
            border: false,
            compact: false
        };
        OpenLayers.Util.extend(this.options, options || {});
        this.options.numSlices = this.options.compact ? 2 : 4;
        if (this._isTransparent()) {
            this.options.blend = false;
        }
    },
    _whichSideTop: function () {
        if (this._hasString(this.options.corners, "all", "top")) {
            return "";
        }
        if (this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0) {
            return "";
        }
        if (this.options.corners.indexOf("tl") >= 0) {
            return "left";
        } else if (this.options.corners.indexOf("tr") >= 0) {
            return "right";
        }
        return "";
    },
    _whichSideBottom: function () {
        if (this._hasString(this.options.corners, "all", "bottom")) {
            return "";
        }
        if (this.options.corners.indexOf("bl") >= 0 && this.options.corners.indexOf("br") >= 0) {
            return "";
        }
        if (this.options.corners.indexOf("bl") >= 0) {
            return "left";
        } else if (this.options.corners.indexOf("br") >= 0) {
            return "right";
        }
        return "";
    },
    _borderColor: function (color, bgColor) {
        if (color == "transparent") {
            return bgColor;
        } else if (this.options.border) {
            return this.options.border;
        } else if (this.options.blend) {
            return this._blend(bgColor, color);
        } else {
            return "";
        }
    },
    _setMargin: function (el, n, corners) {
        var marginSize = this._marginSize(n);
        var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
        if (whichSide == "left") {
            el.style.marginLeft = marginSize + "px";
            el.style.marginRight = "0px";
        } else if (whichSide == "right") {
            el.style.marginRight = marginSize + "px";
            el.style.marginLeft = "0px";
        } else {
            el.style.marginLeft = marginSize + "px";
            el.style.marginRight = marginSize + "px";
        }
    },
    _setBorder: function (el, n, corners) {
        var borderSize = this._borderSize(n);
        var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
        if (whichSide == "left") {
            el.style.borderLeftWidth = borderSize + "px";
            el.style.borderRightWidth = "0px";
        } else if (whichSide == "right") {
            el.style.borderRightWidth = borderSize + "px";
            el.style.borderLeftWidth = "0px";
        } else {
            el.style.borderLeftWidth = borderSize + "px";
            el.style.borderRightWidth = borderSize + "px";
        }
        if (this.options.border != false) {
            el.style.borderLeftWidth = borderSize + "px";
            el.style.borderRightWidth = borderSize + "px";
        }
    },
    _marginSize: function (n) {
        if (this._isTransparent()) {
            return 0;
        }
        var marginSizes = [5, 3, 2, 1];
        var blendedMarginSizes = [3, 2, 1, 0];
        var compactMarginSizes = [2, 1];
        var smBlendedMarginSizes = [1, 0];
        if (this.options.compact && this.options.blend) {
            return smBlendedMarginSizes[n];
        } else if (this.options.compact) {
            return compactMarginSizes[n];
        } else if (this.options.blend) {
            return blendedMarginSizes[n];
        } else {
            return marginSizes[n];
        }
    },
    _borderSize: function (n) {
        var transparentBorderSizes = [5, 3, 2, 1];
        var blendedBorderSizes = [2, 1, 1, 1];
        var compactBorderSizes = [1, 0];
        var actualBorderSizes = [0, 2, 0, 0];
        if (this.options.compact && (this.options.blend || this._isTransparent())) {
            return 1;
        } else if (this.options.compact) {
            return compactBorderSizes[n];
        } else if (this.options.blend) {
            return blendedBorderSizes[n];
        } else if (this.options.border) {
            return actualBorderSizes[n];
        } else if (this._isTransparent()) {
            return transparentBorderSizes[n];
        }
        return 0;
    },
    _hasString: function (str) {
        for (var i = 1; i < arguments.length; i++) if (str.indexOf(arguments[i]) >= 0) {
            return true;
        }
        return false;
    },
    _blend: function (c1, c2) {
        var cc1 = OpenLayers.Rico.Color.createFromHex(c1);
        cc1.blend(OpenLayers.Rico.Color.createFromHex(c2));
        return cc1;
    },
    _background: function (el) {
        try {
            return OpenLayers.Rico.Color.createColorFromBackground(el).asHex();
        } catch (err) {
            return "#ffffff";
        }
    },
    _isTransparent: function () {
        return this.options.color == "transparent";
    },
    _isTopRounded: function () {
        return this._hasString(this.options.corners, "all", "top", "tl", "tr");
    },
    _isBottomRounded: function () {
        return this._hasString(this.options.corners, "all", "bottom", "bl", "br");
    },
    _hasSingleTextChild: function (el) {
        return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3;
    }
};
OpenLayers.Popup.AnchoredBubble = OpenLayers.Class(OpenLayers.Popup.Anchored, {
    rounded: false,
    initialize: function (id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {
        this.padding = new OpenLayers.Bounds(0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE, 0, OpenLayers.Popup.AnchoredBubble.CORNER_SIZE);
        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
    },
    draw: function (px) {
        OpenLayers.Popup.Anchored.prototype.draw.apply(this, arguments);
        this.setContentHTML();
        this.setBackgroundColor();
        this.setOpacity();
        return this.div;
    },
    updateRelativePosition: function () {
        this.setRicoCorners();
    },
    setSize: function (contentSize) {
        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
        this.setRicoCorners();
    },
    setBackgroundColor: function (color) {
        if (color != undefined) {
            this.backgroundColor = color;
        }
        if (this.div != null) {
            if (this.contentDiv != null) {
                this.div.style.background = "transparent";
                OpenLayers.Rico.Corner.changeColor(this.groupDiv, this.backgroundColor);
            }
        }
    },
    setOpacity: function (opacity) {
        OpenLayers.Popup.Anchored.prototype.setOpacity.call(this, opacity);
        if (this.div != null) {
            if (this.groupDiv != null) {
                OpenLayers.Rico.Corner.changeOpacity(this.groupDiv, this.opacity);
            }
        }
    },
    setBorder: function (border) {
        this.border = 0;
    },
    setRicoCorners: function () {
        var corners = this.getCornersToRound(this.relativePosition);
        var options = {
            corners: corners,
            color: this.backgroundColor,
            bgColor: "transparent",
            blend: false
        };
        if (!this.rounded) {
            OpenLayers.Rico.Corner.round(this.div, options);
            this.rounded = true;
        } else {
            OpenLayers.Rico.Corner.reRound(this.groupDiv, options);
            this.setBackgroundColor();
            this.setOpacity();
        }
    },
    getCornersToRound: function () {
        var corners = ['tl', 'tr', 'bl', 'br'];
        var corner = OpenLayers.Bounds.oppositeQuadrant(this.relativePosition);
        OpenLayers.Util.removeItem(corners, corner);
        return corners.join(" ");
    },
    CLASS_NAME: "OpenLayers.Popup.AnchoredBubble"
});
OpenLayers.Popup.AnchoredBubble.CORNER_SIZE = 5;
OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, {
    version: null,
    srsName: "EPSG:4326",
    featureType: null,
    featureNS: null,
    geometryName: "the_geom",
    schema: null,
    featurePrefix: "feature",
    formatOptions: null,
    readFormat: null,
    readOptions: null,
    initialize: function (options) {
        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
        if (!options.format) {
            this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({
                version: this.version,
                featureType: this.featureType,
                featureNS: this.featureNS,
                featurePrefix: this.featurePrefix,
                geometryName: this.geometryName,
                srsName: this.srsName,
                schema: this.schema
            }, this.formatOptions));
        }
        if (!options.geometryName && parseFloat(this.format.version) > 1.0) {
            this.setGeometryName(null);
        }
    },
    destroy: function () {
        if (this.options && !this.options.format) {
            this.format.destroy();
        }
        this.format = null;
        OpenLayers.Protocol.prototype.destroy.apply(this);
    },
    read: function (options) {
        OpenLayers.Protocol.prototype.read.apply(this, arguments);
        options = OpenLayers.Util.extend({}, options);
        OpenLayers.Util.applyDefaults(options, this.options || {});
        var response = new OpenLayers.Protocol.Response({
            requestType: "read"
        });
        var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [this.format.writeNode("wfs:GetFeature", options)]);
        response.priv = OpenLayers.Request.POST({
            url: options.url,
            callback: this.createCallback(this.handleRead, response, options),
            params: options.params,
            headers: options.headers,
            data: data
        });
        return response;
    },
    setFeatureType: function (featureType) {
        this.featureType = featureType;
        this.format.featureType = featureType;
    },
    setGeometryName: function (geometryName) {
        this.geometryName = geometryName;
        this.format.geometryName = geometryName;
    },
    handleRead: function (response, options) {
        options = OpenLayers.Util.extend({}, options);
        OpenLayers.Util.applyDefaults(options, this.options);
        if (options.callback) {
            var request = response.priv;
            if (request.status >= 200 && request.status < 300) {
                var result = this.parseResponse(request, options.readOptions);
                if (result && result.success !== false) {
                    if (options.readOptions && options.readOptions.output == "object") {
                        OpenLayers.Util.extend(response, result);
                    } else {
                        response.features = result;
                    }
                    response.code = OpenLayers.Protocol.Response.SUCCESS;
                } else {
                    response.code = OpenLayers.Protocol.Response.FAILURE;
                    response.error = result;
                }
            } else {
                response.code = OpenLayers.Protocol.Response.FAILURE;
            }
            options.callback.call(options.scope, response);
        }
    },
    parseResponse: function (request, options) {
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        if (!doc || doc.length <= 0) {
            return null;
        }
        var result = (this.readFormat !== null) ? this.readFormat.read(doc) : this.format.read(doc, options);
        if (!this.featureNS) {
            var format = this.readFormat || this.format;
            this.featureNS = format.featureNS;
            format.autoConfig = false;
            if (!this.geometryName) {
                this.setGeometryName(format.geometryName);
            }
        }
        return result;
    },
    commit: function (features, options) {
        options = OpenLayers.Util.extend({}, options);
        OpenLayers.Util.applyDefaults(options, this.options);
        var response = new OpenLayers.Protocol.Response({
            requestType: "commit",
            reqFeatures: features
        });
        response.priv = OpenLayers.Request.POST({
            url: options.url,
            headers: options.headers,
            data: this.format.write(features, options),
            callback: this.createCallback(this.handleCommit, response, options)
        });
        return response;
    },
    handleCommit: function (response, options) {
        if (options.callback) {
            var request = response.priv;
            var data = request.responseXML;
            if (!data || !data.documentElement) {
                data = request.responseText;
            }
            var obj = this.format.read(data) || {};
            response.insertIds = obj.insertIds || [];
            if (obj.success) {
                response.code = OpenLayers.Protocol.Response.SUCCESS;
            } else {
                response.code = OpenLayers.Protocol.Response.FAILURE;
                response.error = obj;
            }
            options.callback.call(options.scope, response);
        }
    },
    filterDelete: function (filter, options) {
        options = OpenLayers.Util.extend({}, options);
        OpenLayers.Util.applyDefaults(options, this.options);
        var response = new OpenLayers.Protocol.Response({
            requestType: "commit"
        });
        var root = this.format.createElementNSPlus("wfs:Transaction", {
            attributes: {
                service: "WFS",
                version: this.version
            }
        });
        var deleteNode = this.format.createElementNSPlus("wfs:Delete", {
            attributes: {
                typeName: (options.featureNS ? this.featurePrefix + ":" : "") + options.featureType
            }
        });
        if (options.featureNS) {
            deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS);
        }
        var filterNode = this.format.writeNode("ogc:Filter", filter);
        deleteNode.appendChild(filterNode);
        root.appendChild(deleteNode);
        var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [root]);
        return OpenLayers.Request.POST({
            url: this.url,
            callback: options.callback ||
            function () {},
            data: data
        });
    },
    abort: function (response) {
        if (response) {
            response.priv.abort();
        }
    },
    CLASS_NAME: "OpenLayers.Protocol.WFS.v1"
});
OpenLayers.Handler.Point = OpenLayers.Class(OpenLayers.Handler, {
    point: null,
    layer: null,
    multi: false,
    mouseDown: false,
    stoppedDown: null,
    lastDown: null,
    lastUp: null,
    persist: false,
    stopDown: false,
    stopUp: false,
    layerOptions: null,
    pixelTolerance: 5,
    touch: false,
    lastTouchPx: null,
    initialize: function (control, callbacks, options) {
        if (!(options && options.layerOptions && options.layerOptions.styleMap)) {
            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
        }
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },
    activate: function () {
        if (!OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            return false;
        }
        var options = OpenLayers.Util.extend({
            displayInLayerSwitcher: false,
            calculateInRange: OpenLayers.Function.True
        }, this.layerOptions);
        this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
        this.map.addLayer(this.layer);
        return true;
    },
    createFeature: function (pixel) {
        var lonlat = this.map.getLonLatFromPixel(pixel);
        var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
        this.point = new OpenLayers.Feature.Vector(geometry);
        this.callback("create", [this.point.geometry, this.point]);
        this.point.geometry.clearBounds();
        this.layer.addFeatures([this.point], {
            silent: true
        });
    },
    deactivate: function () {
        if (!OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            return false;
        }
        this.cancel();
        if (this.layer.map != null) {
            this.destroyFeature(true);
            this.layer.destroy(false);
        }
        this.layer = null;
        this.touch = false;
        return true;
    },
    destroyFeature: function (force) {
        if (this.layer && (force || !this.persist)) {
            this.layer.destroyFeatures();
        }
        this.point = null;
    },
    destroyPersistedFeature: function () {
        var layer = this.layer;
        if (layer && layer.features.length > 1) {
            this.layer.features[0].destroy();
        }
    },
    finalize: function (cancel) {
        var key = cancel ? "cancel" : "done";
        this.mouseDown = false;
        this.lastDown = null;
        this.lastUp = null;
        this.lastTouchPx = null;
        this.callback(key, [this.geometryClone()]);
        this.destroyFeature(cancel);
    },
    cancel: function () {
        this.finalize(true);
    },
    click: function (evt) {
        OpenLayers.Event.stop(evt);
        return false;
    },
    dblclick: function (evt) {
        OpenLayers.Event.stop(evt);
        return false;
    },
    modifyFeature: function (pixel) {
        if (!this.point) {
            this.createFeature(pixel);
        }
        var lonlat = this.map.getLonLatFromPixel(pixel);
        this.point.geometry.x = lonlat.lon;
        this.point.geometry.y = lonlat.lat;
        this.callback("modify", [this.point.geometry, this.point, false]);
        this.point.geometry.clearBounds();
        this.drawFeature();
    },
    drawFeature: function () {
        this.layer.drawFeature(this.point, this.style);
    },
    getGeometry: function () {
        var geometry = this.point && this.point.geometry;
        if (geometry && this.multi) {
            geometry = new OpenLayers.Geometry.MultiPoint([geometry]);
        }
        return geometry;
    },
    geometryClone: function () {
        var geom = this.getGeometry();
        return geom && geom.clone();
    },
    mousedown: function (evt) {
        return this.down(evt);
    },
    touchstart: function (evt) {
        if (!this.touch) {
            this.touch = true;
            this.map.events.un({
                mousedown: this.mousedown,
                mouseup: this.mouseup,
                mousemove: this.mousemove,
                click: this.click,
                dblclick: this.dblclick,
                scope: this
            });
        }
        this.lastTouchPx = evt.xy;
        return this.down(evt);
    },
    mousemove: function (evt) {
        return this.move(evt);
    },
    touchmove: function (evt) {
        this.lastTouchPx = evt.xy;
        return this.move(evt);
    },
    mouseup: function (evt) {
        return this.up(evt);
    },
    touchend: function (evt) {
        evt.xy = this.lastTouchPx;
        return this.up(evt);
    },
    down: function (evt) {
        this.mouseDown = true;
        this.lastDown = evt.xy;
        if (!this.touch) {
            this.modifyFeature(evt.xy);
        }
        this.stoppedDown = this.stopDown;
        return !this.stopDown;
    },
    move: function (evt) {
        if (!this.touch && (!this.mouseDown || this.stoppedDown)) {
            this.modifyFeature(evt.xy);
        }
        return true;
    },
    up: function (evt) {
        this.mouseDown = false;
        this.stoppedDown = this.stopDown;
        if (!this.checkModifiers(evt)) {
            return true;
        }
        if (this.lastUp && this.lastUp.equals(evt.xy)) {
            return true;
        }
        if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy, this.pixelTolerance)) {
            if (this.touch) {
                this.modifyFeature(evt.xy);
            }
            if (this.persist) {
                this.destroyPersistedFeature();
            }
            this.lastUp = evt.xy;
            this.finalize();
            return !this.stopUp;
        } else {
            return true;
        }
    },
    mouseout: function (evt) {
        if (OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
            this.stoppedDown = this.stopDown;
            this.mouseDown = false;
        }
    },
    passesTolerance: function (pixel1, pixel2, tolerance) {
        var passes = true;
        if (tolerance != null && pixel1 && pixel2) {
            var dist = pixel1.distanceTo(pixel2);
            if (dist > tolerance) {
                passes = false;
            }
        }
        return passes;
    },
    CLASS_NAME: "OpenLayers.Handler.Point"
});
OpenLayers.Handler.Path = OpenLayers.Class(OpenLayers.Handler.Point, {
    line: null,
    maxVertices: null,
    doubleTouchTolerance: 20,
    freehand: false,
    freehandToggle: 'shiftKey',
    timerId: null,
    redoStack: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.Point.prototype.initialize.apply(this, arguments);
    },
    createFeature: function (pixel) {
        var lonlat = this.map.getLonLatFromPixel(pixel);
        var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
        this.point = new OpenLayers.Feature.Vector(geometry);
        this.line = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([this.point.geometry]));
        this.callback("create", [this.point.geometry, this.getSketch()]);
        this.point.geometry.clearBounds();
        this.layer.addFeatures([this.line, this.point], {
            silent: true
        });
    },
    destroyFeature: function (force) {
        OpenLayers.Handler.Point.prototype.destroyFeature.call(this, force);
        this.line = null;
    },
    destroyPersistedFeature: function () {
        var layer = this.layer;
        if (layer && layer.features.length > 2) {
            this.layer.features[0].destroy();
        }
    },
    removePoint: function () {
        if (this.point) {
            this.layer.removeFeatures([this.point]);
        }
    },
    addPoint: function (pixel) {
        this.layer.removeFeatures([this.point]);
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat));
        this.line.geometry.addComponent(this.point.geometry, this.line.geometry.components.length);
        this.layer.addFeatures([this.point]);
        this.callback("point", [this.point.geometry, this.getGeometry()]);
        this.callback("modify", [this.point.geometry, this.getSketch()]);
        this.drawFeature();
        delete this.redoStack;
    },
    insertXY: function (x, y) {
        this.line.geometry.addComponent(new OpenLayers.Geometry.Point(x, y), this.getCurrentPointIndex());
        this.drawFeature();
        delete this.redoStack;
    },
    insertDeltaXY: function (dx, dy) {
        var previousIndex = this.getCurrentPointIndex() - 1;
        var p0 = this.line.geometry.components[previousIndex];
        if (p0 && !isNaN(p0.x) && !isNaN(p0.y)) {
            this.insertXY(p0.x + dx, p0.y + dy);
        }
    },
    insertDirectionLength: function (direction, length) {
        direction *= Math.PI / 180;
        var dx = length * Math.cos(direction);
        var dy = length * Math.sin(direction);
        this.insertDeltaXY(dx, dy);
    },
    insertDeflectionLength: function (deflection, length) {
        var previousIndex = this.getCurrentPointIndex() - 1;
        if (previousIndex > 0) {
            var p1 = this.line.geometry.components[previousIndex];
            var p0 = this.line.geometry.components[previousIndex - 1];
            var theta = Math.atan2(p1.y - p0.y, p1.x - p0.x);
            this.insertDirectionLength((theta * 180 / Math.PI) + deflection, length);
        }
    },
    getCurrentPointIndex: function () {
        return this.line.geometry.components.length - 1;
    },
    undo: function () {
        var geometry = this.line.geometry;
        var components = geometry.components;
        var index = this.getCurrentPointIndex() - 1;
        var target = components[index];
        var undone = geometry.removeComponent(target);
        if (undone) {
            if (!this.redoStack) {
                this.redoStack = [];
            }
            this.redoStack.push(target);
            this.drawFeature();
        }
        return undone;
    },
    redo: function () {
        var target = this.redoStack && this.redoStack.pop();
        if (target) {
            this.line.geometry.addComponent(target, this.getCurrentPointIndex());
            this.drawFeature();
        }
        return !!target;
    },
    freehandMode: function (evt) {
        return (this.freehandToggle && evt[this.freehandToggle]) ? !this.freehand : this.freehand;
    },
    modifyFeature: function (pixel, drawing) {
        if (!this.line) {
            this.createFeature(pixel);
        }
        var lonlat = this.control.map.getLonLatFromPixel(pixel);
        this.point.geometry.x = lonlat.lon;
        this.point.geometry.y = lonlat.lat;
        this.callback("modify", [this.point.geometry, this.getSketch(), drawing]);
        this.point.geometry.clearBounds();
        this.drawFeature();
    },
    drawFeature: function () {
        this.layer.drawFeature(this.line, this.style);
        this.layer.drawFeature(this.point, this.style);
    },
    getSketch: function () {
        return this.line;
    },
    getGeometry: function () {
        var geometry = this.line && this.line.geometry;
        if (geometry && this.multi) {
            geometry = new OpenLayers.Geometry.MultiLineString([geometry]);
        }
        return geometry;
    },
    touchstart: function (evt) {
        if (this.timerId && this.passesTolerance(this.lastTouchPx, evt.xy, this.doubleTouchTolerance)) {
            this.finishGeometry();
            window.clearTimeout(this.timerId);
            this.timerId = null;
            return false;
        } else {
            if (this.timerId) {
                window.clearTimeout(this.timerId);
                this.timerId = null;
            }
            this.timerId = window.setTimeout(OpenLayers.Function.bind(function () {
                this.timerId = null;
            }, this), 300);
            return OpenLayers.Handler.Point.prototype.touchstart.call(this, evt);
        }
    },
    down: function (evt) {
        var stopDown = this.stopDown;
        if (this.freehandMode(evt)) {
            stopDown = true;
        }
        if (!this.touch && (!this.lastDown || !this.passesTolerance(this.lastDown, evt.xy, this.pixelTolerance))) {
            this.modifyFeature(evt.xy, !! this.lastUp);
        }
        this.mouseDown = true;
        this.lastDown = evt.xy;
        this.stoppedDown = stopDown;
        return !stopDown;
    },
    move: function (evt) {
        if (this.stoppedDown && this.freehandMode(evt)) {
            if (this.persist) {
                this.destroyPersistedFeature();
            }
            this.addPoint(evt.xy);
            return false;
        }
        if (!this.touch && (!this.mouseDown || this.stoppedDown)) {
            this.modifyFeature(evt.xy, !! this.lastUp);
        }
        return true;
    },
    up: function (evt) {
        if (this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {
            if (this.stoppedDown && this.freehandMode(evt)) {
                if (this.persist) {
                    this.destroyPersistedFeature();
                }
                this.removePoint();
                this.finalize();
            } else {
                if (this.passesTolerance(this.lastDown, evt.xy, this.pixelTolerance)) {
                    if (this.touch) {
                        this.modifyFeature(evt.xy);
                    }
                    if (this.lastUp == null && this.persist) {
                        this.destroyPersistedFeature();
                    }
                    this.addPoint(evt.xy);
                    this.lastUp = evt.xy;
                    if (this.line.geometry.components.length === this.maxVertices + 1) {
                        this.finishGeometry();
                    }
                }
            }
        }
        this.stoppedDown = this.stopDown;
        this.mouseDown = false;
        return !this.stopUp;
    },
    finishGeometry: function () {
        var index = this.line.geometry.components.length - 1;
        this.line.geometry.removeComponent(this.line.geometry.components[index]);
        this.removePoint();
        this.finalize();
    },
    dblclick: function (evt) {
        if (!this.freehandMode(evt)) {
            this.finishGeometry();
        }
        return false;
    },
    CLASS_NAME: "OpenLayers.Handler.Path"
});
OpenLayers.Layer.GML = OpenLayers.Class(OpenLayers.Layer.Vector, {
    loaded: false,
    format: null,
    formatOptions: null,
    initialize: function (name, url, options) {
        var newArguments = [];
        newArguments.push(name, options);
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
        this.url = url;
    },
    setVisibility: function (visibility, noEvent) {
        OpenLayers.Layer.Vector.prototype.setVisibility.apply(this, arguments);
        if (this.visibility && !this.loaded) {
            this.loadGML();
        }
    },
    moveTo: function (bounds, zoomChanged, minor) {
        OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
        if (this.visibility && !this.loaded) {
            this.loadGML();
        }
    },
    loadGML: function () {
        if (!this.loaded) {
            this.events.triggerEvent("loadstart");
            OpenLayers.Request.GET({
                url: this.url,
                success: this.requestSuccess,
                failure: this.requestFailure,
                scope: this
            });
            this.loaded = true;
        }
    },
    setUrl: function (url) {
        this.url = url;
        this.destroyFeatures();
        this.loaded = false;
        this.loadGML();
    },
    requestSuccess: function (request) {
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        var options = {};
        OpenLayers.Util.extend(options, this.formatOptions);
        if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
            options.externalProjection = this.projection;
            options.internalProjection = this.map.getProjectionObject();
        }
        var gml = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
        this.addFeatures(gml.read(doc));
        this.events.triggerEvent("loadend");
    },
    requestFailure: function (request) {
        OpenLayers.Console.userError(OpenLayers.i18n("errorLoadingGML", {
            'url': this.url
        }));
        this.events.triggerEvent("loadend");
    },
    CLASS_NAME: "OpenLayers.Layer.GML"
});
OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    layerOptions: null,
    layerParams: null,
    read: function (data, options) {
        var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, arguments);
        var map;
        if (options && options.map) {
            this.context = context;
            if (options.map instanceof OpenLayers.Map) {
                map = this.mergeContextToMap(context, options.map);
            } else {
                var mapOptions = options.map;
                if (OpenLayers.Util.isElement(mapOptions) || typeof mapOptions == "string") {
                    mapOptions = {
                        div: mapOptions
                    };
                }
                map = this.contextToMap(context, mapOptions);
            }
        } else {
            map = context;
        }
        return map;
    },
    getLayerFromContext: function (layerContext) {
        var i, len;
        var options = {
            queryable: layerContext.queryable,
            visibility: layerContext.visibility,
            maxExtent: layerContext.maxExtent,
            metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, {
                styles: layerContext.styles,
                formats: layerContext.formats,
                "abstract": layerContext["abstract"],
                dataURL: layerContext.dataURL
            }),
            numZoomLevels: layerContext.numZoomLevels,
            units: layerContext.units,
            isBaseLayer: layerContext.isBaseLayer,
            opacity: layerContext.opacity,
            displayInLayerSwitcher: layerContext.displayInLayerSwitcher,
            singleTile: layerContext.singleTile,
            tileSize: (layerContext.tileSize) ? new OpenLayers.Size(layerContext.tileSize.width, layerContext.tileSize.height) : undefined,
            minScale: layerContext.minScale || layerContext.maxScaleDenominator,
            maxScale: layerContext.maxScale || layerContext.minScaleDenominator,
            srs: layerContext.srs,
            dimensions: layerContext.dimensions,
            metadataURL: layerContext.metadataURL
        };
        if (this.layerOptions) {
            OpenLayers.Util.applyDefaults(options, this.layerOptions);
        }
        var params = {
            layers: layerContext.name,
            transparent: layerContext.transparent,
            version: layerContext.version
        };
        if (layerContext.formats && layerContext.formats.length > 0) {
            params.format = layerContext.formats[0].value;
            for (i = 0, len = layerContext.formats.length; i < len; i++) {
                var format = layerContext.formats[i];
                if (format.current == true) {
                    params.format = format.value;
                    break;
                }
            }
        }
        if (layerContext.styles && layerContext.styles.length > 0) {
            for (i = 0, len = layerContext.styles.length; i < len; i++) {
                var style = layerContext.styles[i];
                if (style.current == true) {
                    if (style.href) {
                        params.sld = style.href;
                    } else if (style.body) {
                        params.sld_body = style.body;
                    } else {
                        params.styles = style.name;
                    }
                    break;
                }
            }
        }
        if (this.layerParams) {
            OpenLayers.Util.applyDefaults(params, this.layerParams);
        }
        var layer = null;
        var service = layerContext.service;
        if (service == OpenLayers.Format.Context.serviceTypes.WFS) {
            options.strategies = [new OpenLayers.Strategy.BBOX()];
            options.protocol = new OpenLayers.Protocol.WFS({
                url: layerContext.url,
                featurePrefix: layerContext.name.split(":")[0],
                featureType: layerContext.name.split(":").pop()
            });
            layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);
        } else if (service == OpenLayers.Format.Context.serviceTypes.KML) {
            options.strategies = [new OpenLayers.Strategy.Fixed()];
            options.protocol = new OpenLayers.Protocol.HTTP({
                url: layerContext.url,
                format: new OpenLayers.Format.KML()
            });
            layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);
        } else if (service == OpenLayers.Format.Context.serviceTypes.GML) {
            options.strategies = [new OpenLayers.Strategy.Fixed()];
            options.protocol = new OpenLayers.Protocol.HTTP({
                url: layerContext.url,
                format: new OpenLayers.Format.GML()
            });
            layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);
        } else if (layerContext.features) {
            layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options);
            layer.addFeatures(layerContext.features);
        } else if (layerContext.categoryLayer !== true) {
            layer = new OpenLayers.Layer.WMS(layerContext.title || layerContext.name, layerContext.url, params, options);
        }
        return layer;
    },
    getLayersFromContext: function (layersContext) {
        var layers = [];
        for (var i = 0, len = layersContext.length; i < len; i++) {
            var layer = this.getLayerFromContext(layersContext[i]);
            if (layer !== null) {
                layers.push(layer);
            }
        }
        return layers;
    },
    contextToMap: function (context, options) {
        options = OpenLayers.Util.applyDefaults({
            maxExtent: context.maxExtent,
            projection: context.projection,
            units: context.units
        }, options);
        if (options.maxExtent) {
            options.maxResolution = options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;
        }
        var metadata = {
            contactInformation: context.contactInformation,
            "abstract": context["abstract"],
            keywords: context.keywords,
            logo: context.logo,
            descriptionURL: context.descriptionURL
        }
        options.metadata = metadata;
        var map = new OpenLayers.Map(options);
        map.addLayers(this.getLayersFromContext(context.layersContext));
        map.setCenter(context.bounds.getCenterLonLat(), map.getZoomForExtent(context.bounds, true));
        return map;
    },
    mergeContextToMap: function (context, map) {
        map.addLayers(this.getLayersFromContext(context.layersContext));
        return map;
    },
    write: function (obj, options) {
        obj = this.toContext(obj);
        return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Format.Context"
});
OpenLayers.Format.Context.serviceTypes = {
    "WMS": "urn:ogc:serviceType:WMS",
    "WFS": "urn:ogc:serviceType:WFS",
    "WCS": "urn:ogc:serviceType:WCS",
    "GML": "urn:ogc:serviceType:GML",
    "SLD": "urn:ogc:serviceType:SLD",
    "FES": "urn:ogc:serviceType:FES",
    "KML": "urn:ogc:serviceType:KML"
};
OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, {
    defaultVersion: "1.1.0",
    layerToContext: function (layer) {
        var parser = this.getParser();
        var layerContext = {
            queryable: layer.queryable,
            visibility: layer.visibility,
            name: layer.params["LAYERS"],
            title: layer.name,
            "abstract": layer.metadata["abstract"],
            dataURL: layer.metadata.dataURL,
            metadataURL: layer.metadataURL,
            server: {
                version: layer.params["VERSION"],
                url: layer.url
            },
            maxExtent: layer.maxExtent,
            transparent: layer.params["TRANSPARENT"],
            numZoomLevels: layer.numZoomLevels,
            units: layer.units,
            isBaseLayer: layer.isBaseLayer,
            opacity: layer.opacity,
            displayInLayerSwitcher: layer.displayInLayerSwitcher,
            singleTile: layer.singleTile,
            tileSize: (layer.singleTile || !layer.tileSize) ? undefined : {
                width: layer.tileSize.w,
                height: layer.tileSize.h
            },
            minScale: (layer.options.resolutions || layer.options.scales || layer.options.maxResolution || layer.options.minScale) ? layer.minScale : undefined,
            maxScale: (layer.options.resolutions || layer.options.scales || layer.options.minResolution || layer.options.maxScale) ? layer.maxScale : undefined,
            formats: [],
            styles: [],
            srs: layer.srs,
            dimensions: layer.dimensions
        };
        if (layer.metadata.servertitle) {
            layerContext.server.title = layer.metadata.servertitle;
        }
        if (layer.metadata.formats && layer.metadata.formats.length > 0) {
            for (var i = 0, len = layer.metadata.formats.length; i < len; i++) {
                var format = layer.metadata.formats[i];
                layerContext.formats.push({
                    value: format.value,
                    current: (format.value == layer.params["FORMAT"])
                });
            }
        } else {
            layerContext.formats.push({
                value: layer.params["FORMAT"],
                current: true
            });
        }
        if (layer.metadata.styles && layer.metadata.styles.length > 0) {
            for (var i = 0, len = layer.metadata.styles.length; i < len; i++) {
                var style = layer.metadata.styles[i];
                if ((style.href == layer.params["SLD"]) || (style.body == layer.params["SLD_BODY"]) || (style.name == layer.params["STYLES"])) {
                    style.current = true;
                } else {
                    style.current = false;
                }
                layerContext.styles.push(style);
            }
        } else {
            layerContext.styles.push({
                href: layer.params["SLD"],
                body: layer.params["SLD_BODY"],
                name: layer.params["STYLES"] || parser.defaultStyleName,
                title: parser.defaultStyleTitle,
                current: true
            });
        }
        return layerContext;
    },
    toContext: function (obj) {
        var context = {};
        var layers = obj.layers;
        if (obj.CLASS_NAME == "OpenLayers.Map") {
            var metadata = obj.metadata || {};
            context.size = obj.getSize();
            context.bounds = obj.getExtent();
            context.projection = obj.projection;
            context.title = obj.title;
            context.keywords = metadata.keywords;
            context["abstract"] = metadata["abstract"];
            context.logo = metadata.logo;
            context.descriptionURL = metadata.descriptionURL;
            context.contactInformation = metadata.contactInformation;
            context.maxExtent = obj.maxExtent;
        } else {
            OpenLayers.Util.applyDefaults(context, obj);
            if (context.layers != undefined) {
                delete(context.layers);
            }
        }
        if (context.layersContext == undefined) {
            context.layersContext = [];
        }
        if (layers != undefined && OpenLayers.Util.isArray(layers)) {
            for (var i = 0, len = layers.length; i < len; i++) {
                var layer = layers[i];
                if (layer instanceof OpenLayers.Layer.WMS) {
                    context.layersContext.push(this.layerToContext(layer));
                }
            }
        }
        return context;
    },
    CLASS_NAME: "OpenLayers.Format.WMC"
});
OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ol: "http://openlayers.org/context",
        wmc: "http://www.opengis.net/context",
        sld: "http://www.opengis.net/sld",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    schemaLocation: "",
    getNamespacePrefix: function (uri) {
        var prefix = null;
        if (uri == null) {
            prefix = this.namespaces[this.defaultPrefix];
        } else {
            for (prefix in this.namespaces) {
                if (this.namespaces[prefix] == uri) {
                    break;
                }
            }
        }
        return prefix;
    },
    defaultPrefix: "wmc",
    rootPrefix: null,
    defaultStyleName: "",
    defaultStyleTitle: "Default",
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var root = data.documentElement;
        this.rootPrefix = root.prefix;
        var context = {
            version: root.getAttribute("version")
        };
        this.runChildNodes(context, root);
        return context;
    },
    runChildNodes: function (obj, node) {
        var children = node.childNodes;
        var childNode, processor, prefix, local;
        for (var i = 0, len = children.length; i < len; ++i) {
            childNode = children[i];
            if (childNode.nodeType == 1) {
                prefix = this.getNamespacePrefix(childNode.namespaceURI);
                local = childNode.nodeName.split(":").pop();
                processor = this["read_" + prefix + "_" + local];
                if (processor) {
                    processor.apply(this, [obj, childNode]);
                }
            }
        }
    },
    read_wmc_General: function (context, node) {
        this.runChildNodes(context, node);
    },
    read_wmc_BoundingBox: function (context, node) {
        context.projection = node.getAttribute("SRS");
        context.bounds = new OpenLayers.Bounds(parseFloat(node.getAttribute("minx")), parseFloat(node.getAttribute("miny")), parseFloat(node.getAttribute("maxx")), parseFloat(node.getAttribute("maxy")));
    },
    read_wmc_LayerList: function (context, node) {
        context.layersContext = [];
        this.runChildNodes(context, node);
    },
    read_wmc_Layer: function (context, node) {
        var layerContext = {
            visibility: (node.getAttribute("hidden") != "1"),
            queryable: (node.getAttribute("queryable") == "1"),
            formats: [],
            styles: [],
            metadata: {}
        };
        this.runChildNodes(layerContext, node);
        context.layersContext.push(layerContext);
    },
    read_wmc_Extension: function (obj, node) {
        this.runChildNodes(obj, node);
    },
    read_ol_units: function (layerContext, node) {
        layerContext.units = this.getChildValue(node);
    },
    read_ol_maxExtent: function (obj, node) {
        var bounds = new OpenLayers.Bounds(node.getAttribute("minx"), node.getAttribute("miny"), node.getAttribute("maxx"), node.getAttribute("maxy"));
        obj.maxExtent = bounds;
    },
    read_ol_transparent: function (layerContext, node) {
        layerContext.transparent = this.getChildValue(node);
    },
    read_ol_numZoomLevels: function (layerContext, node) {
        layerContext.numZoomLevels = parseInt(this.getChildValue(node));
    },
    read_ol_opacity: function (layerContext, node) {
        layerContext.opacity = parseFloat(this.getChildValue(node));
    },
    read_ol_singleTile: function (layerContext, node) {
        layerContext.singleTile = (this.getChildValue(node) == "true");
    },
    read_ol_tileSize: function (layerContext, node) {
        var obj = {
            "width": node.getAttribute("width"),
            "height": node.getAttribute("height")
        };
        layerContext.tileSize = obj;
    },
    read_ol_isBaseLayer: function (layerContext, node) {
        layerContext.isBaseLayer = (this.getChildValue(node) == "true");
    },
    read_ol_displayInLayerSwitcher: function (layerContext, node) {
        layerContext.displayInLayerSwitcher = (this.getChildValue(node) == "true");
    },
    read_wmc_Server: function (layerContext, node) {
        layerContext.version = node.getAttribute("version");
        layerContext.url = this.getOnlineResource_href(node);
        layerContext.metadata.servertitle = node.getAttribute("title");
    },
    read_wmc_FormatList: function (layerContext, node) {
        this.runChildNodes(layerContext, node);
    },
    read_wmc_Format: function (layerContext, node) {
        var format = {
            value: this.getChildValue(node)
        };
        if (node.getAttribute("current") == "1") {
            format.current = true;
        }
        layerContext.formats.push(format);
    },
    read_wmc_StyleList: function (layerContext, node) {
        this.runChildNodes(layerContext, node);
    },
    read_wmc_Style: function (layerContext, node) {
        var style = {};
        this.runChildNodes(style, node);
        if (node.getAttribute("current") == "1") {
            style.current = true;
        }
        layerContext.styles.push(style);
    },
    read_wmc_SLD: function (style, node) {
        this.runChildNodes(style, node);
    },
    read_sld_StyledLayerDescriptor: function (sld, node) {
        var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
        sld.body = xml;
    },
    read_sld_FeatureTypeStyle: function (sld, node) {
        var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]);
        sld.body = xml;
    },
    read_wmc_OnlineResource: function (obj, node) {
        obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href");
    },
    read_wmc_Name: function (obj, node) {
        var name = this.getChildValue(node);
        if (name) {
            obj.name = name;
        }
    },
    read_wmc_Title: function (obj, node) {
        var title = this.getChildValue(node);
        if (title) {
            obj.title = title;
        }
    },
    read_wmc_MetadataURL: function (layerContext, node) {
        layerContext.metadataURL = this.getOnlineResource_href(node);
    },
    read_wmc_KeywordList: function (context, node) {
        context.keywords = [];
        this.runChildNodes(context.keywords, node);
    },
    read_wmc_Keyword: function (keywords, node) {
        keywords.push(this.getChildValue(node));
    },
    read_wmc_Abstract: function (obj, node) {
        var abst = this.getChildValue(node);
        if (abst) {
            obj["abstract"] = abst;
        }
    },
    read_wmc_LogoURL: function (context, node) {
        context.logo = {
            width: node.getAttribute("width"),
            height: node.getAttribute("height"),
            format: node.getAttribute("format"),
            href: this.getOnlineResource_href(node)
        };
    },
    read_wmc_DescriptionURL: function (context, node) {
        context.descriptionURL = this.getOnlineResource_href(node);
    },
    read_wmc_ContactInformation: function (obj, node) {
        var contact = {};
        this.runChildNodes(contact, node);
        obj.contactInformation = contact;
    },
    read_wmc_ContactPersonPrimary: function (contact, node) {
        var personPrimary = {};
        this.runChildNodes(personPrimary, node);
        contact.personPrimary = personPrimary;
    },
    read_wmc_ContactPerson: function (primaryPerson, node) {
        var person = this.getChildValue(node);
        if (person) {
            primaryPerson.person = person;
        }
    },
    read_wmc_ContactOrganization: function (primaryPerson, node) {
        var organization = this.getChildValue(node);
        if (organization) {
            primaryPerson.organization = organization;
        }
    },
    read_wmc_ContactPosition: function (contact, node) {
        var position = this.getChildValue(node);
        if (position) {
            contact.position = position;
        }
    },
    read_wmc_ContactAddress: function (contact, node) {
        var contactAddress = {};
        this.runChildNodes(contactAddress, node);
        contact.contactAddress = contactAddress;
    },
    read_wmc_AddressType: function (contactAddress, node) {
        var type = this.getChildValue(node);
        if (type) {
            contactAddress.type = type;
        }
    },
    read_wmc_Address: function (contactAddress, node) {
        var address = this.getChildValue(node);
        if (address) {
            contactAddress.address = address;
        }
    },
    read_wmc_City: function (contactAddress, node) {
        var city = this.getChildValue(node);
        if (city) {
            contactAddress.city = city;
        }
    },
    read_wmc_StateOrProvince: function (contactAddress, node) {
        var stateOrProvince = this.getChildValue(node);
        if (stateOrProvince) {
            contactAddress.stateOrProvince = stateOrProvince;
        }
    },
    read_wmc_PostCode: function (contactAddress, node) {
        var postcode = this.getChildValue(node);
        if (postcode) {
            contactAddress.postcode = postcode;
        }
    },
    read_wmc_Country: function (contactAddress, node) {
        var country = this.getChildValue(node);
        if (country) {
            contactAddress.country = country;
        }
    },
    read_wmc_ContactVoiceTelephone: function (contact, node) {
        var phone = this.getChildValue(node);
        if (phone) {
            contact.phone = phone;
        }
    },
    read_wmc_ContactFacsimileTelephone: function (contact, node) {
        var fax = this.getChildValue(node);
        if (fax) {
            contact.fax = fax;
        }
    },
    read_wmc_ContactElectronicMailAddress: function (contact, node) {
        var email = this.getChildValue(node);
        if (email) {
            contact.email = email;
        }
    },
    read_wmc_DataURL: function (layerContext, node) {
        layerContext.dataURL = this.getOnlineResource_href(node);
    },
    read_wmc_LegendURL: function (style, node) {
        var legend = {
            width: node.getAttribute('width'),
            height: node.getAttribute('height'),
            format: node.getAttribute('format'),
            href: this.getOnlineResource_href(node)
        };
        style.legend = legend;
    },
    read_wmc_DimensionList: function (layerContext, node) {
        layerContext.dimensions = {};
        this.runChildNodes(layerContext.dimensions, node);
    },
    read_wmc_Dimension: function (dimensions, node) {
        var name = node.getAttribute("name").toLowerCase();
        var dim = {
            name: name,
            units: node.getAttribute("units") || "",
            unitSymbol: node.getAttribute("unitSymbol") || "",
            userValue: node.getAttribute("userValue") || "",
            nearestValue: node.getAttribute("nearestValue") === "1",
            multipleValues: node.getAttribute("multipleValues") === "1",
            current: node.getAttribute("current") === "1",
            "default": node.getAttribute("default") || ""
        };
        var values = this.getChildValue(node);
        dim.values = values.split(",");
        dimensions[dim.name] = dim;
    },
    write: function (context, options) {
        var root = this.createElementDefaultNS("ViewContext");
        this.setAttributes(root, {
            version: this.VERSION,
            id: (options && typeof options.id == "string") ? options.id : OpenLayers.Util.createUniqueID("OpenLayers_Context_")
        });
        this.setAttributeNS(root, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation);
        root.appendChild(this.write_wmc_General(context));
        root.appendChild(this.write_wmc_LayerList(context));
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    createElementDefaultNS: function (name, childValue, attributes) {
        var node = this.createElementNS(this.namespaces[this.defaultPrefix], name);
        if (childValue) {
            node.appendChild(this.createTextNode(childValue));
        }
        if (attributes) {
            this.setAttributes(node, attributes);
        }
        return node;
    },
    setAttributes: function (node, obj) {
        var value;
        for (var name in obj) {
            value = obj[name].toString();
            if (value.match(/[A-Z]/)) {
                this.setAttributeNS(node, null, name, value);
            } else {
                node.setAttribute(name, value);
            }
        }
    },
    write_wmc_General: function (context) {
        var node = this.createElementDefaultNS("General");
        if (context.size) {
            node.appendChild(this.createElementDefaultNS("Window", null, {
                width: context.size.w,
                height: context.size.h
            }));
        }
        var bounds = context.bounds;
        node.appendChild(this.createElementDefaultNS("BoundingBox", null, {
            minx: bounds.left.toPrecision(18),
            miny: bounds.bottom.toPrecision(18),
            maxx: bounds.right.toPrecision(18),
            maxy: bounds.top.toPrecision(18),
            SRS: context.projection
        }));
        node.appendChild(this.createElementDefaultNS("Title", context.title));
        if (context.keywords) {
            node.appendChild(this.write_wmc_KeywordList(context.keywords));
        }
        if (context["abstract"]) {
            node.appendChild(this.createElementDefaultNS("Abstract", context["abstract"]));
        }
        if (context.logo) {
            node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo));
        }
        if (context.descriptionURL) {
            node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL));
        }
        if (context.contactInformation) {
            node.appendChild(this.write_wmc_ContactInformation(context.contactInformation));
        }
        node.appendChild(this.write_ol_MapExtension(context));
        return node;
    },
    write_wmc_KeywordList: function (keywords) {
        var node = this.createElementDefaultNS("KeywordList");
        for (var i = 0, len = keywords.length; i < len; i++) {
            node.appendChild(this.createElementDefaultNS("Keyword", keywords[i]));
        }
        return node;
    },
    write_wmc_ContactInformation: function (contact) {
        var node = this.createElementDefaultNS("ContactInformation");
        if (contact.personPrimary) {
            node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary));
        }
        if (contact.position) {
            node.appendChild(this.createElementDefaultNS("ContactPosition", contact.position));
        }
        if (contact.contactAddress) {
            node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress));
        }
        if (contact.phone) {
            node.appendChild(this.createElementDefaultNS("ContactVoiceTelephone", contact.phone));
        }
        if (contact.fax) {
            node.appendChild(this.createElementDefaultNS("ContactFacsimileTelephone", contact.fax));
        }
        if (contact.email) {
            node.appendChild(this.createElementDefaultNS("ContactElectronicMailAddress", contact.email));
        }
        return node;
    },
    write_wmc_ContactPersonPrimary: function (personPrimary) {
        var node = this.createElementDefaultNS("ContactPersonPrimary");
        if (personPrimary.person) {
            node.appendChild(this.createElementDefaultNS("ContactPerson", personPrimary.person));
        }
        if (personPrimary.organization) {
            node.appendChild(this.createElementDefaultNS("ContactOrganization", personPrimary.organization));
        }
        return node;
    },
    write_wmc_ContactAddress: function (contactAddress) {
        var node = this.createElementDefaultNS("ContactAddress");
        if (contactAddress.type) {
            node.appendChild(this.createElementDefaultNS("AddressType", contactAddress.type));
        }
        if (contactAddress.address) {
            node.appendChild(this.createElementDefaultNS("Address", contactAddress.address));
        }
        if (contactAddress.city) {
            node.appendChild(this.createElementDefaultNS("City", contactAddress.city));
        }
        if (contactAddress.stateOrProvince) {
            node.appendChild(this.createElementDefaultNS("StateOrProvince", contactAddress.stateOrProvince));
        }
        if (contactAddress.postcode) {
            node.appendChild(this.createElementDefaultNS("PostCode", contactAddress.postcode));
        }
        if (contactAddress.country) {
            node.appendChild(this.createElementDefaultNS("Country", contactAddress.country));
        }
        return node;
    },
    write_ol_MapExtension: function (context) {
        var node = this.createElementDefaultNS("Extension");
        var bounds = context.maxExtent;
        if (bounds) {
            var maxExtent = this.createElementNS(this.namespaces.ol, "ol:maxExtent");
            this.setAttributes(maxExtent, {
                minx: bounds.left.toPrecision(18),
                miny: bounds.bottom.toPrecision(18),
                maxx: bounds.right.toPrecision(18),
                maxy: bounds.top.toPrecision(18)
            });
            node.appendChild(maxExtent);
        }
        return node;
    },
    write_wmc_LayerList: function (context) {
        var list = this.createElementDefaultNS("LayerList");
        for (var i = 0, len = context.layersContext.length; i < len; ++i) {
            list.appendChild(this.write_wmc_Layer(context.layersContext[i]));
        }
        return list;
    },
    write_wmc_Layer: function (context) {
        var node = this.createElementDefaultNS("Layer", null, {
            queryable: context.queryable ? "1" : "0",
            hidden: context.visibility ? "0" : "1"
        });
        node.appendChild(this.write_wmc_Server(context));
        node.appendChild(this.createElementDefaultNS("Name", context.name));
        node.appendChild(this.createElementDefaultNS("Title", context.title));
        if (context["abstract"]) {
            node.appendChild(this.createElementDefaultNS("Abstract", context["abstract"]));
        }
        if (context.dataURL) {
            node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL));
        }
        if (context.metadataURL) {
            node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL));
        }
        return node;
    },
    write_wmc_LayerExtension: function (context) {
        var node = this.createElementDefaultNS("Extension");
        var bounds = context.maxExtent;
        var maxExtent = this.createElementNS(this.namespaces.ol, "ol:maxExtent");
        this.setAttributes(maxExtent, {
            minx: bounds.left.toPrecision(18),
            miny: bounds.bottom.toPrecision(18),
            maxx: bounds.right.toPrecision(18),
            maxy: bounds.top.toPrecision(18)
        });
        node.appendChild(maxExtent);
        if (context.tileSize && !context.singleTile) {
            var size = this.createElementNS(this.namespaces.ol, "ol:tileSize");
            this.setAttributes(size, context.tileSize);
            node.appendChild(size);
        }
        var properties = ["transparent", "numZoomLevels", "units", "isBaseLayer", "opacity", "displayInLayerSwitcher", "singleTile"];
        var child;
        for (var i = 0, len = properties.length; i < len; ++i) {
            child = this.createOLPropertyNode(context, properties[i]);
            if (child) {
                node.appendChild(child);
            }
        }
        return node;
    },
    createOLPropertyNode: function (obj, prop) {
        var node = null;
        if (obj[prop] != null) {
            node = this.createElementNS(this.namespaces.ol, "ol:" + prop);
            node.appendChild(this.createTextNode(obj[prop].toString()));
        }
        return node;
    },
    write_wmc_Server: function (context) {
        var server = context.server;
        var node = this.createElementDefaultNS("Server");
        var attributes = {
            service: "OGC:WMS",
            version: server.version
        };
        if (server.title) {
            attributes.title = server.title
        }
        this.setAttributes(node, attributes);
        node.appendChild(this.write_wmc_OnlineResource(server.url));
        return node;
    },
    write_wmc_URLType: function (elName, url, attr) {
        var node = this.createElementDefaultNS(elName);
        node.appendChild(this.write_wmc_OnlineResource(url));
        if (attr) {
            var optionalAttributes = ["width", "height", "format"];
            for (var i = 0; i < optionalAttributes.length; i++) {
                if (optionalAttributes[i] in attr) {
                    node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]);
                }
            }
        }
        return node;
    },
    write_wmc_DimensionList: function (context) {
        var node = this.createElementDefaultNS("DimensionList");
        var required_attributes = {
            name: true,
            units: true,
            unitSymbol: true,
            userValue: true
        };
        for (var dim in context.dimensions) {
            var attributes = {};
            var dimension = context.dimensions[dim];
            for (var name in dimension) {
                if (typeof dimension[name] == "boolean") {
                    attributes[name] = Number(dimension[name]);
                } else {
                    attributes[name] = dimension[name];
                }
            }
            var values = "";
            if (attributes.values) {
                values = attributes.values.join(",");
                delete attributes.values;
            }
            node.appendChild(this.createElementDefaultNS("Dimension", values, attributes));
        }
        return node;
    },
    write_wmc_FormatList: function (context) {
        var node = this.createElementDefaultNS("FormatList");
        for (var i = 0, len = context.formats.length; i < len; i++) {
            var format = context.formats[i];
            node.appendChild(this.createElementDefaultNS("Format", format.value, (format.current && format.current == true) ? {
                current: "1"
            } : null));
        }
        return node;
    },
    write_wmc_StyleList: function (layer) {
        var node = this.createElementDefaultNS("StyleList");
        var styles = layer.styles;
        if (styles && OpenLayers.Util.isArray(styles)) {
            var sld;
            for (var i = 0, len = styles.length; i < len; i++) {
                var s = styles[i];
                var style = this.createElementDefaultNS("Style", null, (s.current && s.current == true) ? {
                    current: "1"
                } : null);
                if (s.href) {
                    sld = this.createElementDefaultNS("SLD");
                    if (s.name) {
                        sld.appendChild(this.createElementDefaultNS("Name", s.name));
                    }
                    if (s.title) {
                        sld.appendChild(this.createElementDefaultNS("Title", s.title));
                    }
                    if (s.legend) {
                        sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
                    }
                    var link = this.write_wmc_OnlineResource(s.href);
                    sld.appendChild(link);
                    style.appendChild(sld);
                } else if (s.body) {
                    sld = this.createElementDefaultNS("SLD");
                    if (s.name) {
                        sld.appendChild(this.createElementDefaultNS("Name", s.name));
                    }
                    if (s.title) {
                        sld.appendChild(this.createElementDefaultNS("Title", s.title));
                    }
                    if (s.legend) {
                        sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
                    }
                    var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]);
                    var imported = doc.documentElement;
                    if (sld.ownerDocument && sld.ownerDocument.importNode) {
                        imported = sld.ownerDocument.importNode(imported, true);
                    }
                    sld.appendChild(imported);
                    style.appendChild(sld);
                } else {
                    style.appendChild(this.createElementDefaultNS("Name", s.name));
                    style.appendChild(this.createElementDefaultNS("Title", s.title));
                    if (s['abstract']) {
                        style.appendChild(this.createElementDefaultNS("Abstract", s['abstract']));
                    }
                    if (s.legend) {
                        style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend));
                    }
                }
                node.appendChild(style);
            }
        }
        return node;
    },
    write_wmc_OnlineResource: function (href) {
        var node = this.createElementDefaultNS("OnlineResource");
        this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple");
        this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href);
        return node;
    },
    getOnlineResource_href: function (node) {
        var object = {};
        var links = node.getElementsByTagName("OnlineResource");
        if (links.length > 0) {
            this.read_wmc_OnlineResource(object, links[0]);
        }
        return object.href;
    },
    CLASS_NAME: "OpenLayers.Format.WMC.v1"
});
OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, {
    slideFactor: 50,
    slideRatio: null,
    initialize: function (options) {
        OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
        var options = {
            slideFactor: this.slideFactor,
            slideRatio: this.slideRatio
        };
        this.addControls([new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)]);
    },
    CLASS_NAME: "OpenLayers.Control.PanPanel"
});
OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, {
    separator: ", ",
    destroy: function () {
        this.map.events.un({
            "removelayer": this.updateAttribution,
            "addlayer": this.updateAttribution,
            "changelayer": this.updateAttribution,
            "changebaselayer": this.updateAttribution,
            scope: this
        });
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        this.map.events.on({
            'changebaselayer': this.updateAttribution,
            'changelayer': this.updateAttribution,
            'addlayer': this.updateAttribution,
            'removelayer': this.updateAttribution,
            scope: this
        });
        this.updateAttribution();
        return this.div;
    },
    updateAttribution: function () {
        var attributions = [];
        if (this.map && this.map.layers) {
            for (var i = 0, len = this.map.layers.length; i < len; i++) {
                var layer = this.map.layers[i];
                if (layer.attribution && layer.getVisibility()) {
                    if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) {
                        attributions.push(layer.attribution);
                    }
                }
            }
            this.div.innerHTML = attributions.join(this.separator);
        }
    },
    CLASS_NAME: "OpenLayers.Control.Attribution"
});
OpenLayers.Renderer.NG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
    labelNodeType: null,
    updateDimensions: function (zoomChanged) {
        var mapExtent = this.map.getExtent();
        var renderExtent = mapExtent.scale(3);
        this.setExtent(renderExtent, true);
        var res = this.getResolution();
        var div = this.rendererRoot.parentNode;
        var layerLeft = parseFloat(div.parentNode.style.left);
        var layerTop = parseFloat(div.parentNode.style.top);
        div.style.left = ((renderExtent.left - mapExtent.left) / res - layerLeft) + "px";
        div.style.top = ((mapExtent.top - renderExtent.top) / res - layerTop) + "px";
    },
    setSize: function () {
        this.map.getExtent() && this.updateDimensions();
    },
    drawFeature: function (feature, style) {
        if (style == null) {
            style = feature.style;
        }
        if (feature.geometry) {
            var rendered = this.drawGeometry(feature.geometry, style, feature.id);
            if (rendered !== false && style.label) {
                var location = feature.geometry.getCentroid();
                this.drawText(feature.id, style, location);
            } else {
                this.removeText(feature.id);
            }
            return rendered;
        }
    },
    drawText: function (featureId, style, location) {
        var label;
        if (typeof featureId !== "string") {
            label = featureId;
        } else {
            label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, this.labelNodeType);
            label._featureId = featureId;
        }
        label._style = style;
        label._x = location.x;
        label._y = location.y;
        if (style.labelXOffset || style.labelYOffset) {
            var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
            var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
            var res = this.getResolution();
            location.move(xOffset * res, yOffset * res);
        }
        if (label.parentNode !== this.textRoot) {
            this.textRoot.appendChild(label);
        }
        return label;
    },
    CLASS_NAME: "OpenLayers.Renderer.NG"
});
OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
    xmlns: "http://www.w3.org/2000/svg",
    xlinkns: "http://www.w3.org/1999/xlink",
    symbolMetrics: null,
    labelNodeType: "g",
    initialize: function (containerID) {
        if (!this.supported()) {
            return;
        }
        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);
        this.symbolMetrics = {};
    },
    supported: function () {
        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
        return (document.implementation && (document.implementation.hasFeature("org.w3c.svg", "1.0") || document.implementation.hasFeature(svgFeature + "SVG", "1.1") || document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1")));
    },
    updateDimensions: function (zoomChanged) {
        OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
        var res = this.getResolution();
        var width = this.extent.getWidth();
        var height = this.extent.getHeight();
        var extentString = [this.extent.left, -this.extent.top, width, height].join(" ");
        this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
        this.rendererRoot.setAttributeNS(null, "width", width / res);
        this.rendererRoot.setAttributeNS(null, "height", height / res);
        if (zoomChanged === true) {
            var i, len;
            var nodes = this.vectorRoot.childNodes;
            for (i = 0, len = nodes.length; i < len; ++i) {
                this.setStyle(nodes[i]);
            }
            var textNodes = this.textRoot.childNodes;
            var label;
            for (i = 0, len = textNodes.length; i < len; ++i) {
                label = textNodes[i];
                this.drawText(label, label._style, new OpenLayers.Geometry.Point(label._x, label._y));
            }
        }
    },
    getNodeType: function (geometry, style) {
        var nodeType = null;
        switch (geometry.CLASS_NAME) {
        case "OpenLayers.Geometry.Point":
            if (style.externalGraphic) {
                nodeType = "image";
            } else if (this.isComplexSymbol(style.graphicName)) {
                nodeType = "svg";
            } else {
                nodeType = "circle";
            }
            break;
        case "OpenLayers.Geometry.Rectangle":
            nodeType = "rect";
            break;
        case "OpenLayers.Geometry.LineString":
            nodeType = "polyline";
            break;
        case "OpenLayers.Geometry.LinearRing":
            nodeType = "polygon";
            break;
        case "OpenLayers.Geometry.Polygon":
        case "OpenLayers.Geometry.Curve":
        case "OpenLayers.Geometry.Surface":
            nodeType = "path";
            break;
        default:
            break;
        }
        return nodeType;
    },
    setStyle: function (node, style, options) {
        style = style || node._style;
        options = options || node._options;
        var resolution = this.getResolution();
        var r = node._radius;
        var widthFactor = resolution;
        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
            node.style.visibility = "";
            if (style.graphic === false) {
                node.style.visibility = "hidden";
            } else if (style.externalGraphic) {
                if (style.graphicTitle) {
                    node.setAttributeNS(null, "title", style.graphicTitle);
                    var label = this.nodeFactory(null, "title");
                    label.textContent = style.graphicTitle;
                    node.appendChild(label);
                }
                if (style.graphicWidth && style.graphicHeight) {
                    node.setAttributeNS(null, "preserveAspectRatio", "none");
                }
                var width = style.graphicWidth || style.graphicHeight;
                var height = style.graphicHeight || style.graphicWidth;
                width = width ? width : style.pointRadius * 2;
                height = height ? height : style.pointRadius * 2;
                width *= resolution;
                height *= resolution;
                var xOffset = (style.graphicXOffset != undefined) ? style.graphicXOffset * resolution : -(0.5 * width);
                var yOffset = (style.graphicYOffset != undefined) ? style.graphicYOffset * resolution : -(0.5 * height);
                var opacity = style.graphicOpacity || style.fillOpacity;
                node.setAttributeNS(null, "x", node._x + xOffset);
                node.setAttributeNS(null, "y", node._y + yOffset);
                node.setAttributeNS(null, "width", width);
                node.setAttributeNS(null, "height", height);
                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
                node.setAttributeNS(null, "style", "opacity: " + opacity);
                node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
            } else if (this.isComplexSymbol(style.graphicName)) {
                var offset = style.pointRadius * 3 * resolution;
                var size = offset * 2;
                var src = this.importSymbol(style.graphicName);
                widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
                var parent = node.parentNode;
                var nextSibling = node.nextSibling;
                if (parent) {
                    parent.removeChild(node);
                }
                node.firstChild && node.removeChild(node.firstChild);
                node.appendChild(src.firstChild.cloneNode(true));
                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
                node.setAttributeNS(null, "width", size);
                node.setAttributeNS(null, "height", size);
                node.setAttributeNS(null, "x", node._x - offset);
                node.setAttributeNS(null, "y", node._y - offset);
                if (nextSibling) {
                    parent.insertBefore(node, nextSibling);
                } else if (parent) {
                    parent.appendChild(node);
                }
            } else {
                node.setAttributeNS(null, "r", style.pointRadius * resolution);
            }
            var rotation = style.rotation;
            if (rotation !== undefined || node._rotation !== undefined) {
                node._rotation = rotation;
                rotation |= 0;
                if (node.nodeName !== "svg") {
                    node.setAttributeNS(null, "transform", ["rotate(", rotation, node._x, node._y, ")"].join(" "));
                } else {
                    var metrics = this.symbolMetrics[src.id];
                    node.firstChild.setAttributeNS(null, "transform", ["rotate(", rotation, metrics.x, metrics.y, ")"].join(" "));
                }
            }
        }
        if (options.isFilled) {
            node.setAttributeNS(null, "fill", style.fillColor);
            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
        } else {
            node.setAttributeNS(null, "fill", "none");
        }
        if (options.isStroked) {
            node.setAttributeNS(null, "stroke", style.strokeColor);
            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
            node.setAttributeNS(null, "stroke-linejoin", "round");
            style.strokeDashstyle && node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style, widthFactor));
        } else {
            node.setAttributeNS(null, "stroke", "none");
        }
        if (style.pointerEvents) {
            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
        }
        if (style.cursor != null) {
            node.setAttributeNS(null, "cursor", style.cursor);
        }
        return node;
    },
    dashStyle: function (style, widthFactor) {
        var w = style.strokeWidth * widthFactor;
        var str = style.strokeDashstyle;
        switch (str) {
        case 'solid':
            return 'none';
        case 'dot':
            return [widthFactor, 4 * w].join();
        case 'dash':
            return [4 * w, 4 * w].join();
        case 'dashdot':
            return [4 * w, 4 * w, widthFactor, 4 * w].join();
        case 'longdash':
            return [8 * w, 4 * w].join();
        case 'longdashdot':
            return [8 * w, 4 * w, widthFactor, 4 * w].join();
        default:
            var parts = OpenLayers.String.trim(str).split(/\s+/g);
            for (var i = 0, ii = parts.length; i < ii; i++) {
                parts[i] = parts[i] * widthFactor;
            }
            return parts.join();
        }
    },
    createNode: function (type, id) {
        var node = document.createElementNS(this.xmlns, type);
        if (id) {
            node.setAttributeNS(null, "id", id);
        }
        return node;
    },
    nodeTypeCompare: function (node, type) {
        return (type == node.nodeName);
    },
    createRenderRoot: function () {
        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
    },
    createRoot: function (suffix) {
        return this.nodeFactory(this.container.id + suffix, "g");
    },
    createDefs: function () {
        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
        this.rendererRoot.appendChild(defs);
        return defs;
    },
    drawPoint: function (node, geometry) {
        return this.drawCircle(node, geometry, 1);
    },
    drawCircle: function (node, geometry, radius) {
        var x = geometry.x;
        var y = -geometry.y;
        node.setAttributeNS(null, "cx", x);
        node.setAttributeNS(null, "cy", y);
        node._x = x;
        node._y = y;
        node._radius = radius;
        return node;
    },
    drawLineString: function (node, geometry) {
        var path = this.getComponentsString(geometry.components);
        node.setAttributeNS(null, "points", path);
        return node;
    },
    drawLinearRing: function (node, geometry) {
        var path = this.getComponentsString(geometry.components);
        node.setAttributeNS(null, "points", path);
        return node;
    },
    drawPolygon: function (node, geometry) {
        var d = [];
        var draw = true;
        var complete = true;
        var linearRingResult, path;
        for (var j = 0, len = geometry.components.length; j < len; j++) {
            d.push("M");
            path = this.getComponentsString(geometry.components[j].components, " ");
            d.push(path);
        }
        d.push("z");
        node.setAttributeNS(null, "d", d.join(" "));
        node.setAttributeNS(null, "fill-rule", "evenodd");
        return node;
    },
    drawRectangle: function (node, geometry) {
        node.setAttributeNS(null, "x", geometry.x);
        node.setAttributeNS(null, "y", -geometry.y);
        node.setAttributeNS(null, "width", geometry.width);
        node.setAttributeNS(null, "height", geometry.height);
        return node;
    },
    drawSurface: function (node, geometry) {
        var d = [];
        var draw = true;
        for (var i = 0, len = geometry.components.length; i < len; i++) {
            if ((i % 3) == 0 && (i / 3) == 0) {
                var component = this.getShortString(geometry.components[i]);
                d.push("M", component);
            } else if ((i % 3) == 1) {
                var component = this.getShortString(geometry.components[i]);
                d.push("C", component);
            } else {
                var component = this.getShortString(geometry.components[i]);
                d.push(component);
            }
        }
        d.push("Z");
        node.setAttributeNS(null, "d", d.join(" "));
        return node;
    },
    drawText: function (featureId, style, location) {
        var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
        var text = g.firstChild || this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
        var res = this.getResolution();
        text.setAttributeNS(null, "x", location.x / res);
        text.setAttributeNS(null, "y", -location.y / res);
        g.setAttributeNS(null, "transform", "scale(" + res + ")");
        if (style.fontColor) {
            text.setAttributeNS(null, "fill", style.fontColor);
        }
        if (style.fontOpacity) {
            text.setAttributeNS(null, "opacity", style.fontOpacity);
        }
        if (style.fontFamily) {
            text.setAttributeNS(null, "font-family", style.fontFamily);
        }
        if (style.fontSize) {
            text.setAttributeNS(null, "font-size", style.fontSize);
        }
        if (style.fontWeight) {
            text.setAttributeNS(null, "font-weight", style.fontWeight);
        }
        if (style.fontStyle) {
            text.setAttributeNS(null, "font-style", style.fontStyle);
        }
        if (style.labelSelect === true) {
            text.setAttributeNS(null, "pointer-events", "visible");
            text._featureId = featureId;
        } else {
            text.setAttributeNS(null, "pointer-events", "none");
        }
        var align = style.labelAlign || "cm";
        text.setAttributeNS(null, "text-anchor", OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
        if (OpenLayers.IS_GECKO === true) {
            text.setAttributeNS(null, "dominant-baseline", OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
        }
        var labelRows = style.label.split('\n');
        var numRows = labelRows.length;
        while (text.childNodes.length > numRows) {
            text.removeChild(text.lastChild);
        }
        for (var i = 0; i < numRows; i++) {
            var tspan = text.childNodes[i] || this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
            if (style.labelSelect === true) {
                tspan._featureId = featureId;
            }
            if (OpenLayers.IS_GECKO === false) {
                tspan.setAttributeNS(null, "baseline-shift", OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
            }
            tspan.setAttribute("x", location.x / res);
            if (i == 0) {
                var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
                if (vfactor == null) {
                    vfactor = -.5;
                }
                tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em");
            } else {
                tspan.setAttribute("dy", "1em");
            }
            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
            if (!tspan.parentNode) {
                text.appendChild(tspan);
            }
        }
        if (!text.parentNode) {
            g.appendChild(text);
        }
        return g;
    },
    getComponentsString: function (components, separator) {
        var len = components.length;
        var strings = new Array(len);
        for (var i = 0; i < len; i++) {
            strings[i] = this.getShortString(components[i]);
        }
        return strings.join(separator || ",");
    },
    getShortString: function (point) {
        return point.x + "," + (-point.y);
    },
    importSymbol: function (graphicName) {
        if (!this.defs) {
            this.defs = this.createDefs();
        }
        var id = this.container.id + "-" + graphicName;
        var existing = document.getElementById(id);
        if (existing != null) {
            return existing;
        }
        var symbol = OpenLayers.Renderer.symbol[graphicName];
        if (!symbol) {
            throw new Error(graphicName + ' is not a valid symbol name');
        }
        var symbolNode = this.nodeFactory(id, "symbol");
        var node = this.nodeFactory(null, "polygon");
        symbolNode.appendChild(node);
        var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
        var points = [];
        var x, y;
        for (var i = 0, len = symbol.length; i < len; i = i + 2) {
            x = symbol[i];
            y = symbol[i + 1];
            symbolExtent.left = Math.min(symbolExtent.left, x);
            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
            symbolExtent.right = Math.max(symbolExtent.right, x);
            symbolExtent.top = Math.max(symbolExtent.top, y);
            points.push(x, ",", y);
        }
        node.setAttributeNS(null, "points", points.join(" "));
        var width = symbolExtent.getWidth();
        var height = symbolExtent.getHeight();
        var viewBox = [symbolExtent.left - width, symbolExtent.bottom - height, width * 3, height * 3];
        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
        this.symbolMetrics[id] = {
            size: Math.max(width, height),
            x: symbolExtent.getCenterLonLat().lon,
            y: symbolExtent.getCenterLonLat().lat
        };
        this.defs.appendChild(symbolNode);
        return symbolNode;
    },
    getFeatureIdFromEvent: function (evt) {
        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
        if (!featureId) {
            var target = evt.target;
            featureId = target.parentNode && target != this.rendererRoot && target.parentNode._featureId;
        }
        return featureId;
    },
    CLASS_NAME: "OpenLayers.Renderer.SVG2"
});
OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
    "l": "start",
    "r": "end",
    "b": "bottom",
    "t": "hanging"
};
OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
    "t": "-70%",
    "b": "0"
};
OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
    "t": 0,
    "b": -1
};
OpenLayers.Renderer.SVG2.preventDefault = function (e) {
    e.preventDefault && e.preventDefault();
};
OpenLayers.Kinetic = OpenLayers.Class({
    threshold: 0,
    interval: 10,
    deceleration: 0.0035,
    nbPoints: 100,
    delay: 200,
    points: undefined,
    timerId: undefined,
    initialize: function (options) {
        OpenLayers.Util.extend(this, options);
    },
    begin: function () {
        clearInterval(this.timerId);
        this.timerId = undefined;
        this.points = [];
    },
    update: function (xy) {
        this.points.unshift({
            xy: xy,
            tick: new Date().getTime()
        });
        if (this.points.length > this.nbPoints) {
            this.points.pop();
        }
    },
    end: function (xy) {
        var last, now = new Date().getTime();
        for (var i = 0, l = this.points.length, point; i < l; i++) {
            point = this.points[i];
            if (now - point.tick > this.delay) {
                break;
            }
            last = point;
        }
        if (!last) {
            return;
        }
        var time = new Date().getTime() - last.tick;
        var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2));
        var speed = dist / time;
        if (speed == 0 || speed < this.threshold) {
            return;
        }
        var theta = Math.asin((xy.y - last.xy.y) / dist);
        if (last.xy.x <= xy.x) {
            theta = Math.PI - theta;
        }
        return {
            speed: speed,
            theta: theta
        };
    },
    move: function (info, callback) {
        var v0 = info.speed;
        var fx = Math.cos(info.theta);
        var fy = -Math.sin(info.theta);
        var time = 0;
        var initialTime = new Date().getTime();
        var lastX = 0;
        var lastY = 0;
        var timerCallback = function () {
                if (this.timerId == null) {
                    return;
                }
                time += this.interval;
                var realTime = new Date().getTime() - initialTime;
                var t = (time + realTime) / 2.0;
                var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t;
                var x = p * fx;
                var y = p * fy;
                var args = {};
                args.end = false;
                var v = -this.deceleration * t + v0;
                if (v <= 0) {
                    clearInterval(this.timerId);
                    this.timerId = null;
                    args.end = true;
                }
                args.x = x - lastX;
                args.y = y - lastY;
                lastX = x;
                lastY = y;
                callback(args.x, args.y, args.end);
            };
        this.timerId = window.setInterval(OpenLayers.Function.bind(timerCallback, this), this.interval);
    },
    CLASS_NAME: "OpenLayers.Kinetic"
});
OpenLayers.ProxyHost = "";
OpenLayers.nullHandler = function (request) {
    OpenLayers.Console.userError(OpenLayers.i18n("unhandledRequest", {
        'statusText': request.statusText
    }));
};
OpenLayers.loadURL = function (uri, params, caller, onComplete, onFailure) {
    if (typeof params == 'string') {
        params = OpenLayers.Util.getParameters(params);
    }
    var success = (onComplete) ? onComplete : OpenLayers.nullHandler;
    var failure = (onFailure) ? onFailure : OpenLayers.nullHandler;
    return OpenLayers.Request.GET({
        url: uri,
        params: params,
        success: success,
        failure: failure,
        scope: caller
    });
};
OpenLayers.parseXMLString = function (text) {
    var index = text.indexOf('<');
    if (index > 0) {
        text = text.substring(index);
    }
    var ajaxResponse = OpenLayers.Util.Try(function () {
        var xmldom = new ActiveXObject('Microsoft.XMLDOM');
        xmldom.loadXML(text);
        return xmldom;
    }, function () {
        return new DOMParser().parseFromString(text, 'text/xml');
    }, function () {
        var req = new XMLHttpRequest();
        req.open("GET", "data:" + "text/xml" + ";charset=utf-8," + encodeURIComponent(text), false);
        if (req.overrideMimeType) {
            req.overrideMimeType("text/xml");
        }
        req.send(null);
        return req.responseXML;
    });
    return ajaxResponse;
};
OpenLayers.Ajax = {
    emptyFunction: function () {},
    getTransport: function () {
        return OpenLayers.Util.Try(function () {
            return new XMLHttpRequest();
        }, function () {
            return new ActiveXObject('Msxml2.XMLHTTP');
        }, function () {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }) || false;
    },
    activeRequestCount: 0
};
OpenLayers.Ajax.Responders = {
    responders: [],
    register: function (responderToAdd) {
        for (var i = 0; i < this.responders.length; i++) {
            if (responderToAdd == this.responders[i]) {
                return;
            }
        }
        this.responders.push(responderToAdd);
    },
    unregister: function (responderToRemove) {
        OpenLayers.Util.removeItem(this.reponders, responderToRemove);
    },
    dispatch: function (callback, request, transport) {
        var responder;
        for (var i = 0; i < this.responders.length; i++) {
            responder = this.responders[i];
            if (responder[callback] && typeof responder[callback] == 'function') {
                try {
                    responder[callback].apply(responder, [request, transport]);
                } catch (e) {}
            }
        }
    }
};
OpenLayers.Ajax.Responders.register({
    onCreate: function () {
        OpenLayers.Ajax.activeRequestCount++;
    },
    onComplete: function () {
        OpenLayers.Ajax.activeRequestCount--;
    }
});
OpenLayers.Ajax.Base = OpenLayers.Class({
    initialize: function (options) {
        this.options = {
            method: 'post',
            asynchronous: true,
            contentType: 'application/xml',
            parameters: ''
        };
        OpenLayers.Util.extend(this.options, options || {});
        this.options.method = this.options.method.toLowerCase();
        if (typeof this.options.parameters == 'string') {
            this.options.parameters = OpenLayers.Util.getParameters(this.options.parameters);
        }
    }
});
OpenLayers.Ajax.Request = OpenLayers.Class(OpenLayers.Ajax.Base, {
    _complete: false,
    initialize: function (url, options) {
        OpenLayers.Ajax.Base.prototype.initialize.apply(this, [options]);
        if (OpenLayers.ProxyHost && OpenLayers.String.startsWith(url, "http")) {
            url = OpenLayers.ProxyHost + encodeURIComponent(url);
        }
        this.transport = OpenLayers.Ajax.getTransport();
        this.request(url);
    },
    request: function (url) {
        this.url = url;
        this.method = this.options.method;
        var params = OpenLayers.Util.extend({}, this.options.parameters);
        if (this.method != 'get' && this.method != 'post') {
            params['_method'] = this.method;
            this.method = 'post';
        }
        this.parameters = params;
        if (params = OpenLayers.Util.getParameterString(params)) {
            if (this.method == 'get') {
                this.url += ((this.url.indexOf('?') > -1) ? '&' : '?') + params;
            } else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
                params += '&_=';
            }
        }
        try {
            var response = new OpenLayers.Ajax.Response(this);
            if (this.options.onCreate) {
                this.options.onCreate(response);
            }
            OpenLayers.Ajax.Responders.dispatch('onCreate', this, response);
            this.transport.open(this.method.toUpperCase(), this.url, this.options.asynchronous);
            if (this.options.asynchronous) {
                window.setTimeout(OpenLayers.Function.bind(this.respondToReadyState, this, 1), 10);
            }
            this.transport.onreadystatechange = OpenLayers.Function.bind(this.onStateChange, this);
            this.setRequestHeaders();
            this.body = this.method == 'post' ? (this.options.postBody || params) : null;
            this.transport.send(this.body);
            if (!this.options.asynchronous && this.transport.overrideMimeType) {
                this.onStateChange();
            }
        } catch (e) {
            this.dispatchException(e);
        }
    },
    onStateChange: function () {
        var readyState = this.transport.readyState;
        if (readyState > 1 && !((readyState == 4) && this._complete)) {
            this.respondToReadyState(this.transport.readyState);
        }
    },
    setRequestHeaders: function () {
        var headers = {
            'X-Requested-With': 'XMLHttpRequest',
            'Accept': 'text/javascript, text/html, application/xml, text/xml, */*',
            'OpenLayers': true
        };
        if (this.method == 'post') {
            headers['Content-type'] = this.options.contentType + (this.options.encoding ? '; charset=' + this.options.encoding : '');
            if (this.transport.overrideMimeType && (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0, 2005])[1] < 2005) {
                headers['Connection'] = 'close';
            }
        }
        if (typeof this.options.requestHeaders == 'object') {
            var extras = this.options.requestHeaders;
            if (typeof extras.push == 'function') {
                for (var i = 0, length = extras.length; i < length; i += 2) {
                    headers[extras[i]] = extras[i + 1];
                }
            } else {
                for (var i in extras) {
                    headers[i] = extras[i];
                }
            }
        }
        for (var name in headers) {
            this.transport.setRequestHeader(name, headers[name]);
        }
    },
    success: function () {
        var status = this.getStatus();
        return !status || (status >= 200 && status < 300);
    },
    getStatus: function () {
        try {
            return this.transport.status || 0;
        } catch (e) {
            return 0;
        }
    },
    respondToReadyState: function (readyState) {
        var state = OpenLayers.Ajax.Request.Events[readyState];
        var response = new OpenLayers.Ajax.Response(this);
        if (state == 'Complete') {
            try {
                this._complete = true;
                (this.options['on' + response.status] || this.options['on' + (this.success() ? 'Success' : 'Failure')] || OpenLayers.Ajax.emptyFunction)(response);
            } catch (e) {
                this.dispatchException(e);
            }
            var contentType = response.getHeader('Content-type');
        }
        try {
            (this.options['on' + state] || OpenLayers.Ajax.emptyFunction)(response);
            OpenLayers.Ajax.Responders.dispatch('on' + state, this, response);
        } catch (e) {
            this.dispatchException(e);
        }
        if (state == 'Complete') {
            this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
        }
    },
    getHeader: function (name) {
        try {
            return this.transport.getResponseHeader(name);
        } catch (e) {
            return null;
        }
    },
    dispatchException: function (exception) {
        var handler = this.options.onException;
        if (handler) {
            handler(this, exception);
            OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
        } else {
            var listener = false;
            var responders = OpenLayers.Ajax.Responders.responders;
            for (var i = 0; i < responders.length; i++) {
                if (responders[i].onException) {
                    listener = true;
                    break;
                }
            }
            if (listener) {
                OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
            } else {
                throw exception;
            }
        }
    }
});
OpenLayers.Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
OpenLayers.Ajax.Response = OpenLayers.Class({
    status: 0,
    statusText: '',
    initialize: function (request) {
        this.request = request;
        var transport = this.transport = request.transport,
            readyState = this.readyState = transport.readyState;
        if ((readyState > 2 && !( !! (window.attachEvent && !window.opera))) || readyState == 4) {
            this.status = this.getStatus();
            this.statusText = this.getStatusText();
            this.responseText = transport.responseText == null ? '' : String(transport.responseText);
        }
        if (readyState == 4) {
            var xml = transport.responseXML;
            this.responseXML = xml === undefined ? null : xml;
        }
    },
    getStatus: OpenLayers.Ajax.Request.prototype.getStatus,
    getStatusText: function () {
        try {
            return this.transport.statusText || '';
        } catch (e) {
            return '';
        }
    },
    getHeader: OpenLayers.Ajax.Request.prototype.getHeader,
    getResponseHeader: function (name) {
        return this.transport.getResponseHeader(name);
    }
});
OpenLayers.Ajax.getElementsByTagNameNS = function (parentnode, nsuri, nsprefix, tagname) {
    var elem = null;
    if (parentnode.getElementsByTagNameNS) {
        elem = parentnode.getElementsByTagNameNS(nsuri, tagname);
    } else {
        elem = parentnode.getElementsByTagName(nsprefix + ':' + tagname);
    }
    return elem;
};
OpenLayers.Ajax.serializeXMLToString = function (xmldom) {
    var serializer = new XMLSerializer();
    var data = serializer.serializeToString(xmldom);
    return data;
};
OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, {
    location: null,
    features: null,
    formatOptions: null,
    selectedFeature: null,
    icon: null,
    popupSize: null,
    useFeedTitle: true,
    initialize: function (name, location, options) {
        OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]);
        this.location = location;
        this.features = [];
    },
    destroy: function () {
        OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
        this.clearFeatures();
        this.features = null;
    },
    loadRSS: function () {
        if (!this.loaded) {
            this.events.triggerEvent("loadstart");
            OpenLayers.Request.GET({
                url: this.location,
                success: this.parseData,
                scope: this
            });
            this.loaded = true;
        }
    },
    moveTo: function (bounds, zoomChanged, minor) {
        OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
        if (this.visibility && !this.loaded) {
            this.loadRSS();
        }
    },
    parseData: function (ajaxRequest) {
        var doc = ajaxRequest.responseXML;
        if (!doc || !doc.documentElement) {
            doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText);
        }
        if (this.useFeedTitle) {
            var name = null;
            try {
                name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue;
            } catch (e) {
                name = doc.getElementsByTagName('title')[0].firstChild.nodeValue;
            }
            if (name) {
                this.setName(name);
            }
        }
        var options = {};
        OpenLayers.Util.extend(options, this.formatOptions);
        if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
            options.externalProjection = this.projection;
            options.internalProjection = this.map.getProjectionObject();
        }
        var format = new OpenLayers.Format.GeoRSS(options);
        var features = format.read(doc);
        for (var i = 0, len = features.length; i < len; i++) {
            var data = {};
            var feature = features[i];
            if (!feature.geometry) {
                continue;
            }
            var title = feature.attributes.title ? feature.attributes.title : "Untitled";
            var description = feature.attributes.description ? feature.attributes.description : "No description.";
            var link = feature.attributes.link ? feature.attributes.link : "";
            var location = feature.geometry.getBounds().getCenterLonLat();
            data.icon = this.icon == null ? OpenLayers.Marker.defaultIcon() : this.icon.clone();
            data.popupSize = this.popupSize ? this.popupSize.clone() : new OpenLayers.Size(250, 120);
            if (title || description) {
                data.title = title;
                data.description = description;
                var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>';
                contentHTML += '<div class="olLayerGeoRSSTitle">';
                if (link) {
                    contentHTML += '<a class="link" href="' + link + '" target="_blank">';
                }
                contentHTML += title;
                if (link) {
                    contentHTML += '</a>';
                }
                contentHTML += '</div>';
                contentHTML += '<div style="" class="olLayerGeoRSSDescription">';
                contentHTML += description;
                contentHTML += '</div>';
                data['popupContentHTML'] = contentHTML;
            }
            var feature = new OpenLayers.Feature(this, location, data);
            this.features.push(feature);
            var marker = feature.createMarker();
            marker.events.register('click', feature, this.markerClick);
            this.addMarker(marker);
        }
        this.events.triggerEvent("loadend");
    },
    markerClick: function (evt) {
        var sameMarkerClicked = (this == this.layer.selectedFeature);
        this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
        for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {
            this.layer.map.removePopup(this.layer.map.popups[i]);
        }
        if (!sameMarkerClicked) {
            var popup = this.createPopup();
            OpenLayers.Event.observe(popup.div, "click", OpenLayers.Function.bind(function () {
                for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {
                    this.layer.map.removePopup(this.layer.map.popups[i]);
                }
            }, this));
            this.layer.map.addPopup(popup);
        }
        OpenLayers.Event.stop(evt);
    },
    clearFeatures: function () {
        if (this.features != null) {
            while (this.features.length > 0) {
                var feature = this.features[0];
                OpenLayers.Util.removeItem(this.features, feature);
                feature.destroy();
            }
        }
    },
    CLASS_NAME: "OpenLayers.Layer.GeoRSS"
});
OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, {
    namespaces: {
        sld: "http://www.opengis.net/sld",
        ogc: "http://www.opengis.net/ogc",
        gml: "http://www.opengis.net/gml",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    defaultPrefix: "sld",
    schemaLocation: null,
    multipleSymbolizers: false,
    featureTypeCounter: null,
    defaultSymbolizer: {
        fillColor: "#808080",
        fillOpacity: 1,
        strokeColor: "#000000",
        strokeOpacity: 1,
        strokeWidth: 1,
        strokeDashstyle: "solid",
        pointRadius: 3,
        graphicName: "square"
    },
    initialize: function (options) {
        OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]);
    },
    read: function (data, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var sld = {
            namedLayers: options.namedLayersAsArray === true ? [] : {}
        };
        this.readChildNodes(data, sld);
        return sld;
    },
    readers: OpenLayers.Util.applyDefaults({
        "sld": {
            "StyledLayerDescriptor": function (node, sld) {
                sld.version = node.getAttribute("version");
                this.readChildNodes(node, sld);
            },
            "Name": function (node, obj) {
                obj.name = this.getChildValue(node);
            },
            "Title": function (node, obj) {
                obj.title = this.getChildValue(node);
            },
            "Abstract": function (node, obj) {
                obj.description = this.getChildValue(node);
            },
            "NamedLayer": function (node, sld) {
                var layer = {
                    userStyles: [],
                    namedStyles: []
                };
                this.readChildNodes(node, layer);
                for (var i = 0, len = layer.userStyles.length; i < len; ++i) {
                    layer.userStyles[i].layerName = layer.name;
                }
                if (OpenLayers.Util.isArray(sld.namedLayers)) {
                    sld.namedLayers.push(layer);
                } else {
                    sld.namedLayers[layer.name] = layer;
                }
            },
            "NamedStyle": function (node, layer) {
                layer.namedStyles.push(this.getChildName(node.firstChild));
            },
            "UserStyle": function (node, layer) {
                var obj = {
                    defaultsPerSymbolizer: true,
                    rules: []
                };
                this.featureTypeCounter = -1;
                this.readChildNodes(node, obj);
                var style;
                if (this.multipleSymbolizers) {
                    delete obj.defaultsPerSymbolizer;
                    style = new OpenLayers.Style2(obj);
                } else {
                    style = new OpenLayers.Style(this.defaultSymbolizer, obj);
                }
                layer.userStyles.push(style);
            },
            "IsDefault": function (node, style) {
                if (this.getChildValue(node) == "1") {
                    style.isDefault = true;
                }
            },
            "FeatureTypeStyle": function (node, style) {
                ++this.featureTypeCounter;
                var obj = {
                    rules: this.multipleSymbolizers ? style.rules : []
                };
                this.readChildNodes(node, obj);
                if (!this.multipleSymbolizers) {
                    style.rules = obj.rules;
                }
            },
            "Rule": function (node, obj) {
                var config;
                if (this.multipleSymbolizers) {
                    config = {
                        symbolizers: []
                    };
                }
                var rule = new OpenLayers.Rule(config);
                this.readChildNodes(node, rule);
                obj.rules.push(rule);
            },
            "ElseFilter": function (node, rule) {
                rule.elseFilter = true;
            },
            "MinScaleDenominator": function (node, rule) {
                rule.minScaleDenominator = parseFloat(this.getChildValue(node));
            },
            "MaxScaleDenominator": function (node, rule) {
                rule.maxScaleDenominator = parseFloat(this.getChildValue(node));
            },
            "TextSymbolizer": function (node, rule) {
                var config = {};
                this.readChildNodes(node, config);
                if (this.multipleSymbolizers) {
                    config.zIndex = this.featureTypeCounter;
                    rule.symbolizers.push(new OpenLayers.Symbolizer.Text(config));
                } else {
                    rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Text"]);
                }
            },
            "Label": function (node, symbolizer) {
                var obj = {};
                this.readChildNodes(node, obj);
                if (obj.property) {
                    symbolizer.label = "${" + obj.property + "}";
                } else {
                    var value = this.readOgcExpression(node);
                    if (value) {
                        symbolizer.label = value;
                    }
                }
            },
            "Font": function (node, symbolizer) {
                this.readChildNodes(node, symbolizer);
            },
            "Halo": function (node, symbolizer) {
                var obj = {};
                this.readChildNodes(node, obj);
                symbolizer.haloRadius = obj.haloRadius;
                symbolizer.haloColor = obj.fillColor;
                symbolizer.haloOpacity = obj.fillOpacity;
            },
            "Radius": function (node, symbolizer) {
                var radius = this.readOgcExpression(node);
                if (radius != null) {
                    symbolizer.haloRadius = radius;
                }
            },
            "RasterSymbolizer": function (node, rule) {
                var config = {};
                this.readChildNodes(node, config);
                if (this.multipleSymbolizers) {
                    config.zIndex = this.featureTypeCounter;
                    rule.symbolizers.push(new OpenLayers.Symbolizer.Raster(config));
                } else {
                    rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Raster"]);
                }
            },
            "Geometry": function (node, obj) {
                obj.geometry = {};
                this.readChildNodes(node, obj.geometry);
            },
            "ColorMap": function (node, symbolizer) {
                symbolizer.colorMap = [];
                this.readChildNodes(node, symbolizer.colorMap);
            },
            "ColorMapEntry": function (node, colorMap) {
                var q = node.getAttribute("quantity");
                var o = node.getAttribute("opacity");
                colorMap.push({
                    color: node.getAttribute("color"),
                    quantity: q !== null ? parseFloat(q) : undefined,
                    label: node.getAttribute("label") || undefined,
                    opacity: o !== null ? parseFloat(o) : undefined
                });
            },
            "LineSymbolizer": function (node, rule) {
                var config = {};
                this.readChildNodes(node, config);
                if (this.multipleSymbolizers) {
                    config.zIndex = this.featureTypeCounter;
                    rule.symbolizers.push(new OpenLayers.Symbolizer.Line(config));
                } else {
                    rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Line"]);
                }
            },
            "PolygonSymbolizer": function (node, rule) {
                var config = {
                    fill: false,
                    stroke: false
                };
                if (!this.multipleSymbolizers) {
                    config = rule.symbolizer["Polygon"] || config;
                }
                this.readChildNodes(node, config);
                if (this.multipleSymbolizers) {
                    config.zIndex = this.featureTypeCounter;
                    rule.symbolizers.push(new OpenLayers.Symbolizer.Polygon(config));
                } else {
                    rule.symbolizer["Polygon"] = config;
                }
            },
            "PointSymbolizer": function (node, rule) {
                var config = {
                    fill: false,
                    stroke: false,
                    graphic: false
                };
                if (!this.multipleSymbolizers) {
                    config = rule.symbolizer["Point"] || config;
                }
                this.readChildNodes(node, config);
                if (this.multipleSymbolizers) {
                    config.zIndex = this.featureTypeCounter;
                    rule.symbolizers.push(new OpenLayers.Symbolizer.Point(config));
                } else {
                    rule.symbolizer["Point"] = config;
                }
            },
            "Stroke": function (node, symbolizer) {
                symbolizer.stroke = true;
                this.readChildNodes(node, symbolizer);
            },
            "Fill": function (node, symbolizer) {
                symbolizer.fill = true;
                this.readChildNodes(node, symbolizer);
            },
            "CssParameter": function (node, symbolizer) {
                var cssProperty = node.getAttribute("name");
                var symProperty = this.cssMap[cssProperty];
                if (symProperty) {
                    var value = this.readOgcExpression(node);
                    if (value) {
                        symbolizer[symProperty] = value;
                    }
                }
            },
            "Graphic": function (node, symbolizer) {
                symbolizer.graphic = true;
                var graphic = {};
                this.readChildNodes(node, graphic);
                var properties = ["stroke", "strokeColor", "strokeWidth", "strokeOpacity", "strokeLinecap", "fill", "fillColor", "fillOpacity", "graphicName", "rotation", "graphicFormat"];
                var prop, value;
                for (var i = 0, len = properties.length; i < len; ++i) {
                    prop = properties[i];
                    value = graphic[prop];
                    if (value != undefined) {
                        symbolizer[prop] = value;
                    }
                }
                if (graphic.opacity != undefined) {
                    symbolizer.graphicOpacity = graphic.opacity;
                }
                if (graphic.size != undefined) {
                    symbolizer.pointRadius = graphic.size / 2;
                }
                if (graphic.href != undefined) {
                    symbolizer.externalGraphic = graphic.href;
                }
                if (graphic.rotation != undefined) {
                    symbolizer.rotation = graphic.rotation;
                }
            },
            "ExternalGraphic": function (node, graphic) {
                this.readChildNodes(node, graphic);
            },
            "Mark": function (node, graphic) {
                this.readChildNodes(node, graphic);
            },
            "WellKnownName": function (node, graphic) {
                graphic.graphicName = this.getChildValue(node);
            },
            "Opacity": function (node, obj) {
                var opacity = this.readOgcExpression(node);
                if (opacity) {
                    obj.opacity = opacity;
                }
            },
            "Size": function (node, obj) {
                var size = this.readOgcExpression(node);
                if (size) {
                    obj.size = size;
                }
            },
            "Rotation": function (node, obj) {
                var rotation = this.readOgcExpression(node);
                if (rotation) {
                    obj.rotation = rotation;
                }
            },
            "OnlineResource": function (node, obj) {
                obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href");
            },
            "Format": function (node, graphic) {
                graphic.graphicFormat = this.getChildValue(node);
            }
        }
    }, OpenLayers.Format.Filter.v1_0_0.prototype.readers),
    cssMap: {
        "stroke": "strokeColor",
        "stroke-opacity": "strokeOpacity",
        "stroke-width": "strokeWidth",
        "stroke-linecap": "strokeLinecap",
        "stroke-dasharray": "strokeDashstyle",
        "fill": "fillColor",
        "fill-opacity": "fillOpacity",
        "font-family": "fontFamily",
        "font-size": "fontSize",
        "font-weight": "fontWeight",
        "font-style": "fontStyle"
    },
    getCssProperty: function (sym) {
        var css = null;
        for (var prop in this.cssMap) {
            if (this.cssMap[prop] == sym) {
                css = prop;
                break;
            }
        }
        return css;
    },
    getGraphicFormat: function (href) {
        var format, regex;
        for (var key in this.graphicFormats) {
            if (this.graphicFormats[key].test(href)) {
                format = key;
                break;
            }
        }
        return format || this.defautlGraphicFormat;
    },
    defaultGraphicFormat: "image/png",
    graphicFormats: {
        "image/jpeg": /\.jpe?g$/i,
        "image/gif": /\.gif$/i,
        "image/png": /\.png$/i
    },
    write: function (sld) {
        return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]);
    },
    writers: OpenLayers.Util.applyDefaults({
        "sld": {
            "StyledLayerDescriptor": function (sld) {
                var root = this.createElementNSPlus("sld:StyledLayerDescriptor", {
                    attributes: {
                        "version": this.VERSION,
                        "xsi:schemaLocation": this.schemaLocation
                    }
                });
                root.setAttribute("xmlns:ogc", this.namespaces.ogc);
                root.setAttribute("xmlns:gml", this.namespaces.gml);
                if (sld.name) {
                    this.writeNode("Name", sld.name, root);
                }
                if (sld.title) {
                    this.writeNode("Title", sld.title, root);
                }
                if (sld.description) {
                    this.writeNode("Abstract", sld.description, root);
                }
                if (OpenLayers.Util.isArray(sld.namedLayers)) {
                    for (var i = 0, len = sld.namedLayers.length; i < len; ++i) {
                        this.writeNode("NamedLayer", sld.namedLayers[i], root);
                    }
                } else {
                    for (var name in sld.namedLayers) {
                        this.writeNode("NamedLayer", sld.namedLayers[name], root);
                    }
                }
                return root;
            },
            "Name": function (name) {
                return this.createElementNSPlus("sld:Name", {
                    value: name
                });
            },
            "Title": function (title) {
                return this.createElementNSPlus("sld:Title", {
                    value: title
                });
            },
            "Abstract": function (description) {
                return this.createElementNSPlus("sld:Abstract", {
                    value: description
                });
            },
            "NamedLayer": function (layer) {
                var node = this.createElementNSPlus("sld:NamedLayer");
                this.writeNode("Name", layer.name, node);
                if (layer.namedStyles) {
                    for (var i = 0, len = layer.namedStyles.length; i < len; ++i) {
                        this.writeNode("NamedStyle", layer.namedStyles[i], node);
                    }
                }
                if (layer.userStyles) {
                    for (var i = 0, len = layer.userStyles.length; i < len; ++i) {
                        this.writeNode("UserStyle", layer.userStyles[i], node);
                    }
                }
                return node;
            },
            "NamedStyle": function (name) {
                var node = this.createElementNSPlus("sld:NamedStyle");
                this.writeNode("Name", name, node);
                return node;
            },
            "UserStyle": function (style) {
                var node = this.createElementNSPlus("sld:UserStyle");
                if (style.name) {
                    this.writeNode("Name", style.name, node);
                }
                if (style.title) {
                    this.writeNode("Title", style.title, node);
                }
                if (style.description) {
                    this.writeNode("Abstract", style.description, node);
                }
                if (style.isDefault) {
                    this.writeNode("IsDefault", style.isDefault, node);
                }
                if (this.multipleSymbolizers && style.rules) {
                    var rulesByZ = {
                        0: []
                    };
                    var zValues = [0];
                    var rule, ruleMap, symbolizer, zIndex, clone;
                    for (var i = 0, ii = style.rules.length; i < ii; ++i) {
                        rule = style.rules[i];
                        if (rule.symbolizers) {
                            ruleMap = {};
                            for (var j = 0, jj = rule.symbolizers.length; j < jj; ++j) {
                                symbolizer = rule.symbolizers[j];
                                zIndex = symbolizer.zIndex;
                                if (!(zIndex in ruleMap)) {
                                    clone = rule.clone();
                                    clone.symbolizers = [];
                                    ruleMap[zIndex] = clone;
                                }
                                ruleMap[zIndex].symbolizers.push(symbolizer.clone());
                            }
                            for (zIndex in ruleMap) {
                                if (!(zIndex in rulesByZ)) {
                                    zValues.push(zIndex);
                                    rulesByZ[zIndex] = [];
                                }
                                rulesByZ[zIndex].push(ruleMap[zIndex]);
                            }
                        } else {
                            rulesByZ[0].push(rule.clone());
                        }
                    }
                    zValues.sort();
                    var rules;
                    for (var i = 0, ii = zValues.length; i < ii; ++i) {
                        rules = rulesByZ[zValues[i]];
                        if (rules.length > 0) {
                            clone = style.clone();
                            clone.rules = rulesByZ[zValues[i]];
                            this.writeNode("FeatureTypeStyle", clone, node);
                        }
                    }
                } else {
                    this.writeNode("FeatureTypeStyle", style, node);
                }
                return node;
            },
            "IsDefault": function (bool) {
                return this.createElementNSPlus("sld:IsDefault", {
                    value: (bool) ? "1" : "0"
                });
            },
            "FeatureTypeStyle": function (style) {
                var node = this.createElementNSPlus("sld:FeatureTypeStyle");
                for (var i = 0, len = style.rules.length; i < len; ++i) {
                    this.writeNode("Rule", style.rules[i], node);
                }
                return node;
            },
            "Rule": function (rule) {
                var node = this.createElementNSPlus("sld:Rule");
                if (rule.name) {
                    this.writeNode("Name", rule.name, node);
                }
                if (rule.title) {
                    this.writeNode("Title", rule.title, node);
                }
                if (rule.description) {
                    this.writeNode("Abstract", rule.description, node);
                }
                if (rule.elseFilter) {
                    this.writeNode("ElseFilter", null, node);
                } else if (rule.filter) {
                    this.writeNode("ogc:Filter", rule.filter, node);
                }
                if (rule.minScaleDenominator != undefined) {
                    this.writeNode("MinScaleDenominator", rule.minScaleDenominator, node);
                }
                if (rule.maxScaleDenominator != undefined) {
                    this.writeNode("MaxScaleDenominator", rule.maxScaleDenominator, node);
                }
                var type, symbolizer;
                if (this.multipleSymbolizers && rule.symbolizers) {
                    var symbolizer;
                    for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) {
                        symbolizer = rule.symbolizers[i];
                        type = symbolizer.CLASS_NAME.split(".").pop();
                        this.writeNode(type + "Symbolizer", symbolizer, node);
                    }
                } else {
                    var types = OpenLayers.Style.SYMBOLIZER_PREFIXES;
                    for (var i = 0, len = types.length; i < len; ++i) {
                        type = types[i];
                        symbolizer = rule.symbolizer[type];
                        if (symbolizer) {
                            this.writeNode(type + "Symbolizer", symbolizer, node);
                        }
                    }
                }
                return node;
            },
            "ElseFilter": function () {
                return this.createElementNSPlus("sld:ElseFilter");
            },
            "MinScaleDenominator": function (scale) {
                return this.createElementNSPlus("sld:MinScaleDenominator", {
                    value: scale
                });
            },
            "MaxScaleDenominator": function (scale) {
                return this.createElementNSPlus("sld:MaxScaleDenominator", {
                    value: scale
                });
            },
            "LineSymbolizer": function (symbolizer) {
                var node = this.createElementNSPlus("sld:LineSymbolizer");
                this.writeNode("Stroke", symbolizer, node);
                return node;
            },
            "Stroke": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Stroke");
                if (symbolizer.strokeColor != undefined) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "strokeColor"
                    }, node);
                }
                if (symbolizer.strokeOpacity != undefined) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "strokeOpacity"
                    }, node);
                }
                if (symbolizer.strokeWidth != undefined) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "strokeWidth"
                    }, node);
                }
                if (symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "strokeDashstyle"
                    }, node);
                }
                if (symbolizer.strokeLinecap != undefined) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "strokeLinecap"
                    }, node);
                }
                return node;
            },
            "CssParameter": function (obj) {
                return this.createElementNSPlus("sld:CssParameter", {
                    attributes: {
                        name: this.getCssProperty(obj.key)
                    },
                    value: obj.symbolizer[obj.key]
                });
            },
            "TextSymbolizer": function (symbolizer) {
                var node = this.createElementNSPlus("sld:TextSymbolizer");
                if (symbolizer.label != null) {
                    this.writeNode("Label", symbolizer.label, node);
                }
                if (symbolizer.fontFamily != null || symbolizer.fontSize != null || symbolizer.fontWeight != null || symbolizer.fontStyle != null) {
                    this.writeNode("Font", symbolizer, node);
                }
                if (symbolizer.haloRadius != null || symbolizer.haloColor != null || symbolizer.haloOpacity != null) {
                    this.writeNode("Halo", symbolizer, node);
                }
                if (symbolizer.fillColor != null || symbolizer.fillOpacity != null) {
                    this.writeNode("Fill", symbolizer, node);
                }
                return node;
            },
            "Font": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Font");
                if (symbolizer.fontFamily) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fontFamily"
                    }, node);
                }
                if (symbolizer.fontSize) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fontSize"
                    }, node);
                }
                if (symbolizer.fontWeight) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fontWeight"
                    }, node);
                }
                if (symbolizer.fontStyle) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fontStyle"
                    }, node);
                }
                return node;
            },
            "Label": function (label) {
                var node = this.createElementNSPlus("sld:Label");
                var tokens = label.split("${");
                node.appendChild(this.createTextNode(tokens[0]));
                var item, last;
                for (var i = 1, len = tokens.length; i < len; i++) {
                    item = tokens[i];
                    last = item.indexOf("}");
                    if (last > 0) {
                        this.writeNode("ogc:PropertyName", {
                            property: item.substring(0, last)
                        }, node);
                        node.appendChild(this.createTextNode(item.substring(++last)));
                    } else {
                        node.appendChild(this.createTextNode("${" + item));
                    }
                }
                return node;
            },
            "Halo": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Halo");
                if (symbolizer.haloRadius) {
                    this.writeNode("Radius", symbolizer.haloRadius, node);
                }
                if (symbolizer.haloColor || symbolizer.haloOpacity) {
                    this.writeNode("Fill", {
                        fillColor: symbolizer.haloColor,
                        fillOpacity: symbolizer.haloOpacity
                    }, node);
                }
                return node;
            },
            "Radius": function (value) {
                return this.createElementNSPlus("sld:Radius", {
                    value: value
                });
            },
            "RasterSymbolizer": function (symbolizer) {
                var node = this.createElementNSPlus("sld:RasterSymbolizer");
                if (symbolizer.geometry) {
                    this.writeNode("Geometry", symbolizer.geometry, node);
                }
                if (symbolizer.opacity) {
                    this.writeNode("Opacity", symbolizer.opacity, node);
                }
                if (symbolizer.colorMap) {
                    this.writeNode("ColorMap", symbolizer.colorMap, node);
                }
                return node;
            },
            "Geometry": function (geometry) {
                var node = this.createElementNSPlus("sld:Geometry");
                if (geometry.property) {
                    this.writeNode("ogc:PropertyName", geometry, node);
                }
                return node;
            },
            "ColorMap": function (colorMap) {
                var node = this.createElementNSPlus("sld:ColorMap");
                for (var i = 0, len = colorMap.length; i < len; ++i) {
                    this.writeNode("ColorMapEntry", colorMap[i], node);
                }
                return node;
            },
            "ColorMapEntry": function (colorMapEntry) {
                var node = this.createElementNSPlus("sld:ColorMapEntry");
                var a = colorMapEntry;
                node.setAttribute("color", a.color);
                a.opacity !== undefined && node.setAttribute("opacity", parseFloat(a.opacity));
                a.quantity !== undefined && node.setAttribute("quantity", parseFloat(a.quantity));
                a.label !== undefined && node.setAttribute("label", a.label);
                return node;
            },
            "PolygonSymbolizer": function (symbolizer) {
                var node = this.createElementNSPlus("sld:PolygonSymbolizer");
                if (symbolizer.fill !== false) {
                    this.writeNode("Fill", symbolizer, node);
                }
                if (symbolizer.stroke !== false) {
                    this.writeNode("Stroke", symbolizer, node);
                }
                return node;
            },
            "Fill": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Fill");
                if (symbolizer.fillColor) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fillColor"
                    }, node);
                }
                if (symbolizer.fillOpacity != null) {
                    this.writeNode("CssParameter", {
                        symbolizer: symbolizer,
                        key: "fillOpacity"
                    }, node);
                }
                return node;
            },
            "PointSymbolizer": function (symbolizer) {
                var node = this.createElementNSPlus("sld:PointSymbolizer");
                this.writeNode("Graphic", symbolizer, node);
                return node;
            },
            "Graphic": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Graphic");
                if (symbolizer.externalGraphic != undefined) {
                    this.writeNode("ExternalGraphic", symbolizer, node);
                } else {
                    this.writeNode("Mark", symbolizer, node);
                }
                if (symbolizer.graphicOpacity != undefined) {
                    this.writeNode("Opacity", symbolizer.graphicOpacity, node);
                }
                if (symbolizer.pointRadius != undefined) {
                    this.writeNode("Size", symbolizer.pointRadius * 2, node);
                }
                if (symbolizer.rotation != undefined) {
                    this.writeNode("Rotation", symbolizer.rotation, node);
                }
                return node;
            },
            "ExternalGraphic": function (symbolizer) {
                var node = this.createElementNSPlus("sld:ExternalGraphic");
                this.writeNode("OnlineResource", symbolizer.externalGraphic, node);
                var format = symbolizer.graphicFormat || this.getGraphicFormat(symbolizer.externalGraphic);
                this.writeNode("Format", format, node);
                return node;
            },
            "Mark": function (symbolizer) {
                var node = this.createElementNSPlus("sld:Mark");
                if (symbolizer.graphicName) {
                    this.writeNode("WellKnownName", symbolizer.graphicName, node);
                }
                if (symbolizer.fill !== false) {
                    this.writeNode("Fill", symbolizer, node);
                }
                if (symbolizer.stroke !== false) {
                    this.writeNode("Stroke", symbolizer, node);
                }
                return node;
            },
            "WellKnownName": function (name) {
                return this.createElementNSPlus("sld:WellKnownName", {
                    value: name
                });
            },
            "Opacity": function (value) {
                return this.createElementNSPlus("sld:Opacity", {
                    value: value
                });
            },
            "Size": function (value) {
                return this.createElementNSPlus("sld:Size", {
                    value: value
                });
            },
            "Rotation": function (value) {
                return this.createElementNSPlus("sld:Rotation", {
                    value: value
                });
            },
            "OnlineResource": function (href) {
                return this.createElementNSPlus("sld:OnlineResource", {
                    attributes: {
                        "xlink:type": "simple",
                        "xlink:href": href
                    }
                });
            },
            "Format": function (format) {
                return this.createElementNSPlus("sld:Format", {
                    value: format
                });
            }
        }
    }, OpenLayers.Format.Filter.v1_0_0.prototype.writers),
    CLASS_NAME: "OpenLayers.Format.SLD.v1"
});
OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WMC.v1, {
    VERSION: "1.1.0",
    schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd",
    initialize: function (options) {
        OpenLayers.Format.WMC.v1.prototype.initialize.apply(this, [options]);
    },
    read_sld_MinScaleDenominator: function (layerContext, node) {
        var minScaleDenominator = parseFloat(this.getChildValue(node));
        if (minScaleDenominator > 0) {
            layerContext.maxScale = minScaleDenominator;
        }
    },
    read_sld_MaxScaleDenominator: function (layerContext, node) {
        layerContext.minScale = parseFloat(this.getChildValue(node));
    },
    read_wmc_SRS: function (layerContext, node) {
        if (!("srs" in layerContext)) {
            layerContext.srs = {};
        }
        layerContext.srs[this.getChildValue(node)] = true;
    },
    write_wmc_Layer: function (context) {
        var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this, [context]);
        if (context.maxScale) {
            var minSD = this.createElementNS(this.namespaces.sld, "sld:MinScaleDenominator");
            minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16)));
            node.appendChild(minSD);
        }
        if (context.minScale) {
            var maxSD = this.createElementNS(this.namespaces.sld, "sld:MaxScaleDenominator");
            maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16)));
            node.appendChild(maxSD);
        }
        if (context.srs) {
            for (var name in context.srs) {
                node.appendChild(this.createElementDefaultNS("SRS", name));
            }
        }
        node.appendChild(this.write_wmc_FormatList(context));
        node.appendChild(this.write_wmc_StyleList(context));
        if (context.dimensions) {
            node.appendChild(this.write_wmc_DimensionList(context));
        }
        node.appendChild(this.write_wmc_LayerExtension(context));
        return node;
    },
    CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0"
});
OpenLayers.Format.XLS.v1_1_0 = OpenLayers.Class(OpenLayers.Format.XLS.v1, {
    VERSION: "1.1",
    schemaLocation: "http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd",
    initialize: function (options) {
        OpenLayers.Format.XLS.v1.prototype.initialize.apply(this, [options]);
    },
    CLASS_NAME: "OpenLayers.Format.XLS.v1_1_0"
});
OpenLayers.Format.XLS.v1_1 = OpenLayers.Format.XLS.v1_1_0;
OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
    xmlns: "http://www.w3.org/2000/svg",
    xlinkns: "http://www.w3.org/1999/xlink",
    MAX_PIXEL: 15000,
    translationParameters: null,
    symbolMetrics: null,
    initialize: function (containerID) {
        if (!this.supported()) {
            return;
        }
        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);
        this.translationParameters = {
            x: 0,
            y: 0
        };
        this.symbolMetrics = {};
    },
    supported: function () {
        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
        return (document.implementation && (document.implementation.hasFeature("org.w3c.svg", "1.0") || document.implementation.hasFeature(svgFeature + "SVG", "1.1") || document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1")));
    },
    inValidRange: function (x, y, xyOnly) {
        var left = x + (xyOnly ? 0 : this.translationParameters.x);
        var top = y + (xyOnly ? 0 : this.translationParameters.y);
        return (left >= -this.MAX_PIXEL && left <= this.MAX_PIXEL && top >= -this.MAX_PIXEL && top <= this.MAX_PIXEL);
    },
    setExtent: function (extent, resolutionChanged) {
        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
        var resolution = this.getResolution();
        var left = -extent.left / resolution;
        var top = extent.top / resolution;
        if (resolutionChanged) {
            this.left = left;
            this.top = top;
            var extentString = "0 0 " + this.size.w + " " + this.size.h;
            this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
            this.translate(0, 0);
            return true;
        } else {
            var inRange = this.translate(left - this.left, top - this.top);
            if (!inRange) {
                this.setExtent(extent, true);
            }
            return inRange;
        }
    },
    translate: function (x, y) {
        if (!this.inValidRange(x, y, true)) {
            return false;
        } else {
            var transformString = "";
            if (x || y) {
                transformString = "translate(" + x + "," + y + ")";
            }
            this.root.setAttributeNS(null, "transform", transformString);
            this.translationParameters = {
                x: x,
                y: y
            };
            return true;
        }
    },
    setSize: function (size) {
        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
        this.rendererRoot.setAttributeNS(null, "width", this.size.w);
        this.rendererRoot.setAttributeNS(null, "height", this.size.h);
    },
    getNodeType: function (geometry, style) {
        var nodeType = null;
        switch (geometry.CLASS_NAME) {
        case "OpenLayers.Geometry.Point":
            if (style.externalGraphic) {
                nodeType = "image";
            } else if (this.isComplexSymbol(style.graphicName)) {
                nodeType = "svg";
            } else {
                nodeType = "circle";
            }
            break;
        case "OpenLayers.Geometry.Rectangle":
            nodeType = "rect";
            break;
        case "OpenLayers.Geometry.LineString":
            nodeType = "polyline";
            break;
        case "OpenLayers.Geometry.LinearRing":
            nodeType = "polygon";
            break;
        case "OpenLayers.Geometry.Polygon":
        case "OpenLayers.Geometry.Curve":
        case "OpenLayers.Geometry.Surface":
            nodeType = "path";
            break;
        default:
            break;
        }
        return nodeType;
    },
    setStyle: function (node, style, options) {
        style = style || node._style;
        options = options || node._options;
        var r = parseFloat(node.getAttributeNS(null, "r"));
        var widthFactor = 1;
        var pos;
        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
            node.style.visibility = "";
            if (style.graphic === false) {
                node.style.visibility = "hidden";
            } else if (style.externalGraphic) {
                pos = this.getPosition(node);
                if (style.graphicTitle) {
                    node.setAttributeNS(null, "title", style.graphicTitle);
                    var label = this.nodeFactory(null, "title");
                    label.textContent = style.graphicTitle;
                    node.appendChild(label);
                }
                if (style.graphicWidth && style.graphicHeight) {
                    node.setAttributeNS(null, "preserveAspectRatio", "none");
                }
                var width = style.graphicWidth || style.graphicHeight;
                var height = style.graphicHeight || style.graphicWidth;
                width = width ? width : style.pointRadius * 2;
                height = height ? height : style.pointRadius * 2;
                var xOffset = (style.graphicXOffset != undefined) ? style.graphicXOffset : -(0.5 * width);
                var yOffset = (style.graphicYOffset != undefined) ? style.graphicYOffset : -(0.5 * height);
                var opacity = style.graphicOpacity || style.fillOpacity;
                node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed());
                node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed());
                node.setAttributeNS(null, "width", width);
                node.setAttributeNS(null, "height", height);
                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
                node.setAttributeNS(null, "style", "opacity: " + opacity);
                node.onclick = OpenLayers.Renderer.SVG.preventDefault;
            } else if (this.isComplexSymbol(style.graphicName)) {
                var offset = style.pointRadius * 3;
                var size = offset * 2;
                var src = this.importSymbol(style.graphicName);
                pos = this.getPosition(node);
                widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
                var parent = node.parentNode;
                var nextSibling = node.nextSibling;
                if (parent) {
                    parent.removeChild(node);
                }
                node.firstChild && node.removeChild(node.firstChild);
                node.appendChild(src.firstChild.cloneNode(true));
                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
                node.setAttributeNS(null, "width", size);
                node.setAttributeNS(null, "height", size);
                node.setAttributeNS(null, "x", pos.x - offset);
                node.setAttributeNS(null, "y", pos.y - offset);
                if (nextSibling) {
                    parent.insertBefore(node, nextSibling);
                } else if (parent) {
                    parent.appendChild(node);
                }
            } else {
                node.setAttributeNS(null, "r", style.pointRadius);
            }
            var rotation = style.rotation;
            if ((rotation !== undefined || node._rotation !== undefined) && pos) {
                node._rotation = rotation;
                rotation |= 0;
                if (node.nodeName !== "svg") {
                    node.setAttributeNS(null, "transform", "rotate(" + rotation + " " + pos.x + " " + pos.y + ")");
                } else {
                    var metrics = this.symbolMetrics[src.id];
                    node.firstChild.setAttributeNS(null, "transform", "rotate(" + rotation + " " + metrics[1] + " " + metrics[2] + ")");
                }
            }
        }
        if (options.isFilled) {
            node.setAttributeNS(null, "fill", style.fillColor);
            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
        } else {
            node.setAttributeNS(null, "fill", "none");
        }
        if (options.isStroked) {
            node.setAttributeNS(null, "stroke", style.strokeColor);
            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
            node.setAttributeNS(null, "stroke-linejoin", "round");
            style.strokeDashstyle && node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style, widthFactor));
        } else {
            node.setAttributeNS(null, "stroke", "none");
        }
        if (style.pointerEvents) {
            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
        }
        if (style.cursor != null) {
            node.setAttributeNS(null, "cursor", style.cursor);
        }
        return node;
    },
    dashStyle: function (style, widthFactor) {
        var w = style.strokeWidth * widthFactor;
        var str = style.strokeDashstyle;
        switch (str) {
        case 'solid':
            return 'none';
        case 'dot':
            return [1, 4 * w].join();
        case 'dash':
            return [4 * w, 4 * w].join();
        case 'dashdot':
            return [4 * w, 4 * w, 1, 4 * w].join();
        case 'longdash':
            return [8 * w, 4 * w].join();
        case 'longdashdot':
            return [8 * w, 4 * w, 1, 4 * w].join();
        default:
            return OpenLayers.String.trim(str).replace(/\s+/g, ",");
        }
    },
    createNode: function (type, id) {
        var node = document.createElementNS(this.xmlns, type);
        if (id) {
            node.setAttributeNS(null, "id", id);
        }
        return node;
    },
    nodeTypeCompare: function (node, type) {
        return (type == node.nodeName);
    },
    createRenderRoot: function () {
        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
    },
    createRoot: function (suffix) {
        return this.nodeFactory(this.container.id + suffix, "g");
    },
    createDefs: function () {
        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
        this.rendererRoot.appendChild(defs);
        return defs;
    },
    drawPoint: function (node, geometry) {
        return this.drawCircle(node, geometry, 1);
    },
    drawCircle: function (node, geometry, radius) {
        var resolution = this.getResolution();
        var x = (geometry.x / resolution + this.left);
        var y = (this.top - geometry.y / resolution);
        if (this.inValidRange(x, y)) {
            node.setAttributeNS(null, "cx", x);
            node.setAttributeNS(null, "cy", y);
            node.setAttributeNS(null, "r", radius);
            return node;
        } else {
            return false;
        }
    },
    drawLineString: function (node, geometry) {
        var componentsResult = this.getComponentsString(geometry.components);
        if (componentsResult.path) {
            node.setAttributeNS(null, "points", componentsResult.path);
            return (componentsResult.complete ? node : null);
        } else {
            return false;
        }
    },
    drawLinearRing: function (node, geometry) {
        var componentsResult = this.getComponentsString(geometry.components);
        if (componentsResult.path) {
            node.setAttributeNS(null, "points", componentsResult.path);
            return (componentsResult.complete ? node : null);
        } else {
            return false;
        }
    },
    drawPolygon: function (node, geometry) {
        var d = "";
        var draw = true;
        var complete = true;
        var linearRingResult, path;
        for (var j = 0, len = geometry.components.length; j < len; j++) {
            d += " M";
            linearRingResult = this.getComponentsString(geometry.components[j].components, " ");
            path = linearRingResult.path;
            if (path) {
                d += " " + path;
                complete = linearRingResult.complete && complete;
            } else {
                draw = false;
            }
        }
        d += " z";
        if (draw) {
            node.setAttributeNS(null, "d", d);
            node.setAttributeNS(null, "fill-rule", "evenodd");
            return complete ? node : null;
        } else {
            return false;
        }
    },
    drawRectangle: function (node, geometry) {
        var resolution = this.getResolution();
        var x = (geometry.x / resolution + this.left);
        var y = (this.top - geometry.y / resolution);
        if (this.inValidRange(x, y)) {
            node.setAttributeNS(null, "x", x);
            node.setAttributeNS(null, "y", y);
            node.setAttributeNS(null, "width", geometry.width / resolution);
            node.setAttributeNS(null, "height", geometry.height / resolution);
            return node;
        } else {
            return false;
        }
    },
    drawSurface: function (node, geometry) {
        var d = null;
        var draw = true;
        for (var i = 0, len = geometry.components.length; i < len; i++) {
            if ((i % 3) == 0 && (i / 3) == 0) {
                var component = this.getShortString(geometry.components[i]);
                if (!component) {
                    draw = false;
                }
                d = "M " + component;
            } else if ((i % 3) == 1) {
                var component = this.getShortString(geometry.components[i]);
                if (!component) {
                    draw = false;
                }
                d += " C " + component;
            } else {
                var component = this.getShortString(geometry.components[i]);
                if (!component) {
                    draw = false;
                }
                d += " " + component;
            }
        }
        d += " Z";
        if (draw) {
            node.setAttributeNS(null, "d", d);
            return node;
        } else {
            return false;
        }
    },
    drawText: function (featureId, style, location) {
        var resolution = this.getResolution();
        var x = (location.x / resolution + this.left);
        var y = (location.y / resolution - this.top);
        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
        label.setAttributeNS(null, "x", x);
        label.setAttributeNS(null, "y", -y);
        if (style.fontColor) {
            label.setAttributeNS(null, "fill", style.fontColor);
        }
        if (style.fontOpacity) {
            label.setAttributeNS(null, "opacity", style.fontOpacity);
        }
        if (style.fontFamily) {
            label.setAttributeNS(null, "font-family", style.fontFamily);
        }
        if (style.fontSize) {
            label.setAttributeNS(null, "font-size", style.fontSize);
        }
        if (style.fontWeight) {
            label.setAttributeNS(null, "font-weight", style.fontWeight);
        }
        if (style.fontStyle) {
            label.setAttributeNS(null, "font-style", style.fontStyle);
        }
        if (style.labelSelect === true) {
            label.setAttributeNS(null, "pointer-events", "visible");
            label._featureId = featureId;
        } else {
            label.setAttributeNS(null, "pointer-events", "none");
        }
        var align = style.labelAlign || "cm";
        label.setAttributeNS(null, "text-anchor", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle");
        if (OpenLayers.IS_GECKO === true) {
            label.setAttributeNS(null, "dominant-baseline", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
        }
        var labelRows = style.label.split('\n');
        var numRows = labelRows.length;
        while (label.childNodes.length > numRows) {
            label.removeChild(label.lastChild);
        }
        for (var i = 0; i < numRows; i++) {
            var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
            if (style.labelSelect === true) {
                tspan._featureId = featureId;
                tspan._geometry = location;
                tspan._geometryClass = location.CLASS_NAME;
            }
            if (OpenLayers.IS_GECKO === false) {
                tspan.setAttributeNS(null, "baseline-shift", OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
            }
            tspan.setAttribute("x", x);
            if (i == 0) {
                var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
                if (vfactor == null) {
                    vfactor = -.5;
                }
                tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em");
            } else {
                tspan.setAttribute("dy", "1em");
            }
            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
            if (!tspan.parentNode) {
                label.appendChild(tspan);
            }
        }
        if (!label.parentNode) {
            this.textRoot.appendChild(label);
        }
    },
    getComponentsString: function (components, separator) {
        var renderCmp = [];
        var complete = true;
        var len = components.length;
        var strings = [];
        var str, component;
        for (var i = 0; i < len; i++) {
            component = components[i];
            renderCmp.push(component);
            str = this.getShortString(component);
            if (str) {
                strings.push(str);
            } else {
                if (i > 0) {
                    if (this.getShortString(components[i - 1])) {
                        strings.push(this.clipLine(components[i], components[i - 1]));
                    }
                }
                if (i < len - 1) {
                    if (this.getShortString(components[i + 1])) {
                        strings.push(this.clipLine(components[i], components[i + 1]));
                    }
                }
                complete = false;
            }
        }
        return {
            path: strings.join(separator || ","),
            complete: complete
        };
    },
    clipLine: function (badComponent, goodComponent) {
        if (goodComponent.equals(badComponent)) {
            return "";
        }
        var resolution = this.getResolution();
        var maxX = this.MAX_PIXEL - this.translationParameters.x;
        var maxY = this.MAX_PIXEL - this.translationParameters.y;
        var x1 = goodComponent.x / resolution + this.left;
        var y1 = this.top - goodComponent.y / resolution;
        var x2 = badComponent.x / resolution + this.left;
        var y2 = this.top - badComponent.y / resolution;
        var k;
        if (x2 < -maxX || x2 > maxX) {
            k = (y2 - y1) / (x2 - x1);
            x2 = x2 < 0 ? -maxX : maxX;
            y2 = y1 + (x2 - x1) * k;
        }
        if (y2 < -maxY || y2 > maxY) {
            k = (x2 - x1) / (y2 - y1);
            y2 = y2 < 0 ? -maxY : maxY;
            x2 = x1 + (y2 - y1) * k;
        }
        return x2 + "," + y2;
    },
    getShortString: function (point) {
        var resolution = this.getResolution();
        var x = (point.x / resolution + this.left);
        var y = (this.top - point.y / resolution);
        if (this.inValidRange(x, y)) {
            return x + "," + y;
        } else {
            return false;
        }
    },
    getPosition: function (node) {
        return ({
            x: parseFloat(node.getAttributeNS(null, "cx")),
            y: parseFloat(node.getAttributeNS(null, "cy"))
        });
    },
    importSymbol: function (graphicName) {
        if (!this.defs) {
            this.defs = this.createDefs();
        }
        var id = this.container.id + "-" + graphicName;
        var existing = document.getElementById(id)
        if (existing != null) {
            return existing;
        }
        var symbol = OpenLayers.Renderer.symbol[graphicName];
        if (!symbol) {
            throw new Error(graphicName + ' is not a valid symbol name');
        }
        var symbolNode = this.nodeFactory(id, "symbol");
        var node = this.nodeFactory(null, "polygon");
        symbolNode.appendChild(node);
        var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
        var points = [];
        var x, y;
        for (var i = 0; i < symbol.length; i = i + 2) {
            x = symbol[i];
            y = symbol[i + 1];
            symbolExtent.left = Math.min(symbolExtent.left, x);
            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
            symbolExtent.right = Math.max(symbolExtent.right, x);
            symbolExtent.top = Math.max(symbolExtent.top, y);
            points.push(x, ",", y);
        }
        node.setAttributeNS(null, "points", points.join(" "));
        var width = symbolExtent.getWidth();
        var height = symbolExtent.getHeight();
        var viewBox = [symbolExtent.left - width, symbolExtent.bottom - height, width * 3, height * 3];
        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
        this.symbolMetrics[id] = [Math.max(width, height), symbolExtent.getCenterLonLat().lon, symbolExtent.getCenterLonLat().lat];
        this.defs.appendChild(symbolNode);
        return symbolNode;
    },
    getFeatureIdFromEvent: function (evt) {
        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
        if (!featureId) {
            var target = evt.target;
            featureId = target.parentNode && target != this.rendererRoot && target.parentNode._featureId;
        }
        return featureId;
    },
    CLASS_NAME: "OpenLayers.Renderer.SVG"
});
OpenLayers.Renderer.SVG.LABEL_ALIGN = {
    "l": "start",
    "r": "end",
    "b": "bottom",
    "t": "hanging"
};
OpenLayers.Renderer.SVG.LABEL_VSHIFT = {
    "t": "-70%",
    "b": "0"
};
OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
    "t": 0,
    "b": -1
};
OpenLayers.Renderer.SVG.preventDefault = function (e) {
    e.preventDefault && e.preventDefault();
};
OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class(OpenLayers.Format.SLD.v1, {
    VERSION: "1.0.0",
    schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd",
    initialize: function (options) {
        OpenLayers.Format.SLD.v1.prototype.initialize.apply(this, [options]);
    },
    CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0"
});
OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, {
    defaultVersion: "0.3.1",
    getVersion: function (root, options) {
        var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this, arguments);
        if (version === "0.3.0") {
            version = this.defaultVersion;
        }
        return version;
    },
    toContext: function (obj) {
        var context = {};
        if (obj.CLASS_NAME == "OpenLayers.Map") {
            context.bounds = obj.getExtent();
            context.maxExtent = obj.maxExtent;
            context.projection = obj.projection;
            context.size = obj.getSize();
            context.layers = obj.layers;
        }
        return context;
    },
    CLASS_NAME: "OpenLayers.Format.OWSContext"
});
OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        owc: "http://www.opengis.net/ows-context",
        gml: "http://www.opengis.net/gml",
        kml: "http://www.opengis.net/kml/2.2",
        ogc: "http://www.opengis.net/ogc",
        ows: "http://www.opengis.net/ows",
        sld: "http://www.opengis.net/sld",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    VERSION: "0.3.1",
    schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd",
    defaultPrefix: "owc",
    extractAttributes: true,
    xy: true,
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    featureNS: "http://mapserver.gis.umn.edu/mapserver",
    featureType: 'vector',
    geometryName: 'geometry',
    nestingLayerLookup: null,
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this);
    },
    setNestingPath: function (l) {
        if (l.layersContext) {
            for (var i = 0, len = l.layersContext.length; i < len; i++) {
                var layerContext = l.layersContext[i];
                var nPath = [];
                var nTitle = l.title || "";
                if (l.metadata && l.metadata.nestingPath) {
                    nPath = l.metadata.nestingPath.slice();
                }
                if (nTitle != "") {
                    nPath.push(nTitle);
                }
                layerContext.metadata.nestingPath = nPath;
                if (layerContext.layersContext) {
                    this.setNestingPath(layerContext);
                }
            }
        }
    },
    decomposeNestingPath: function (nPath) {
        var a = [];
        if (OpenLayers.Util.isArray(nPath)) {
            var path = nPath.slice();
            while (path.length > 0) {
                a.push(path.slice());
                path.pop();
            }
            a.reverse();
        }
        return a;
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var context = {};
        this.readNode(data, context);
        this.setNestingPath({
            layersContext: context.layersContext
        });
        var layers = [];
        this.processLayer(layers, context);
        delete context.layersContext;
        context.layersContext = layers;
        return context;
    },
    processLayer: function (layerArray, layer) {
        if (layer.layersContext) {
            for (var i = 0, len = layer.layersContext.length; i < len; i++) {
                var l = layer.layersContext[i];
                layerArray.push(l);
                if (l.layersContext) {
                    this.processLayer(layerArray, l);
                }
            }
        }
    },
    write: function (context, options) {
        var name = "OWSContext";
        this.nestingLayerLookup = {};
        options = options || {};
        OpenLayers.Util.applyDefaults(options, context);
        var root = this.writeNode(name, options);
        this.nestingLayerLookup = null;
        this.setAttributeNS(root, this.namespaces["xsi"], "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [root]);
    },
    readers: {
        "kml": {
            "Document": function (node, obj) {
                obj.features = new OpenLayers.Format.KML({
                    kmlns: this.namespaces.kml,
                    extractStyles: true
                }).read(node);
            }
        },
        "owc": {
            "OWSContext": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "General": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "ResourceList": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Layer": function (node, obj) {
                var layerContext = {
                    metadata: {},
                    visibility: (node.getAttribute("hidden") != "1"),
                    queryable: (node.getAttribute("queryable") == "1"),
                    opacity: ((node.getAttribute("opacity") != null) ? parseFloat(node.getAttribute("opacity")) : null),
                    name: node.getAttribute("name"),
                    categoryLayer: (node.getAttribute("name") == null),
                    formats: [],
                    styles: []
                };
                if (!obj.layersContext) {
                    obj.layersContext = [];
                }
                obj.layersContext.push(layerContext);
                this.readChildNodes(node, layerContext);
            },
            "InlineGeometry": function (node, obj) {
                obj.features = [];
                var elements = this.getElementsByTagNameNS(node, this.namespaces.gml, "featureMember");
                var el;
                if (elements.length >= 1) {
                    el = elements[0];
                }
                if (el && el.firstChild) {
                    var featurenode = (el.firstChild.nextSibling) ? el.firstChild.nextSibling : el.firstChild;
                    this.setNamespace("feature", featurenode.namespaceURI);
                    this.featureType = featurenode.localName || featurenode.nodeName.split(":").pop();
                    this.readChildNodes(node, obj);
                }
            },
            "Server": function (node, obj) {
                if ((!obj.service && !obj.version) || (obj.service != OpenLayers.Format.Context.serviceTypes.WMS)) {
                    obj.service = node.getAttribute("service");
                    obj.version = node.getAttribute("version");
                    this.readChildNodes(node, obj);
                }
            },
            "Name": function (node, obj) {
                obj.name = this.getChildValue(node);
                this.readChildNodes(node, obj);
            },
            "Title": function (node, obj) {
                obj.title = this.getChildValue(node);
                this.readChildNodes(node, obj);
            },
            "StyleList": function (node, obj) {
                this.readChildNodes(node, obj.styles);
            },
            "Style": function (node, obj) {
                var style = {};
                obj.push(style);
                this.readChildNodes(node, style);
            },
            "LegendURL": function (node, obj) {
                var legend = {};
                obj.legend = legend;
                this.readChildNodes(node, legend);
            },
            "OnlineResource": function (node, obj) {
                obj.url = this.getAttributeNS(node, this.namespaces.xlink, "href");
                this.readChildNodes(node, obj);
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows,
        "gml": OpenLayers.Format.GML.v2.prototype.readers.gml,
        "sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld,
        "feature": OpenLayers.Format.GML.v2.prototype.readers.feature
    },
    writers: {
        "owc": {
            "OWSContext": function (options) {
                var node = this.createElementNSPlus("OWSContext", {
                    attributes: {
                        version: this.VERSION,
                        id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_")
                    }
                });
                this.writeNode("General", options, node);
                this.writeNode("ResourceList", options, node);
                return node;
            },
            "General": function (options) {
                var node = this.createElementNSPlus("General");
                this.writeNode("ows:BoundingBox", options, node);
                this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node);
                return node;
            },
            "ResourceList": function (options) {
                var node = this.createElementNSPlus("ResourceList");
                for (var i = 0, len = options.layers.length; i < len; i++) {
                    var layer = options.layers[i];
                    var decomposedPath = this.decomposeNestingPath(layer.metadata.nestingPath);
                    this.writeNode("_Layer", {
                        layer: layer,
                        subPaths: decomposedPath
                    }, node);
                }
                return node;
            },
            "Server": function (options) {
                var node = this.createElementNSPlus("Server", {
                    attributes: {
                        version: options.version,
                        service: options.service
                    }
                });
                this.writeNode("OnlineResource", options, node);
                return node;
            },
            "OnlineResource": function (options) {
                var node = this.createElementNSPlus("OnlineResource", {
                    attributes: {
                        "xlink:href": options.url
                    }
                });
                return node;
            },
            "InlineGeometry": function (layer) {
                var node = this.createElementNSPlus("InlineGeometry");
                this.writeNode("gml:boundedBy", layer.getDataExtent(), node);
                for (var i = 0, len = layer.features.length; i < len; i++) {
                    this.writeNode("gml:featureMember", layer.features[i], node);
                }
                return node;
            },
            "StyleList": function (styles) {
                var node = this.createElementNSPlus("StyleList");
                for (var i = 0, len = styles.length; i < len; i++) {
                    this.writeNode("Style", styles[i], node);
                }
                return node;
            },
            "Style": function (style) {
                var node = this.createElementNSPlus("Style");
                this.writeNode("Name", style, node);
                this.writeNode("Title", style, node);
                if (style.legend) {
                    this.writeNode("LegendURL", style, node);
                }
                return node;
            },
            "Name": function (obj) {
                var node = this.createElementNSPlus("Name", {
                    value: obj.name
                });
                return node;
            },
            "Title": function (obj) {
                var node = this.createElementNSPlus("Title", {
                    value: obj.title
                });
                return node;
            },
            "LegendURL": function (style) {
                var node = this.createElementNSPlus("LegendURL");
                this.writeNode("OnlineResource", style.legend, node);
                return node;
            },
            "_WMS": function (layer) {
                var node = this.createElementNSPlus("Layer", {
                    attributes: {
                        name: layer.params.LAYERS,
                        queryable: layer.queryable ? "1" : "0",
                        hidden: layer.visibility ? "0" : "1",
                        opacity: layer.opacity ? layer.opacity : null
                    }
                });
                this.writeNode("ows:Title", layer.name, node);
                this.writeNode("ows:OutputFormat", layer.params.FORMAT, node);
                this.writeNode("Server", {
                    service: OpenLayers.Format.Context.serviceTypes.WMS,
                    version: layer.params.VERSION,
                    url: layer.url
                }, node);
                if (layer.metadata.styles && layer.metadata.styles.length > 0) {
                    this.writeNode("StyleList", layer.metadata.styles, node);
                }
                return node;
            },
            "_Layer": function (options) {
                var layer, subPaths, node, title;
                layer = options.layer;
                subPaths = options.subPaths;
                node = null;
                title = null;
                if (subPaths.length > 0) {
                    var path = subPaths[0].join("/");
                    var index = path.lastIndexOf("/");
                    node = this.nestingLayerLookup[path];
                    title = (index > 0) ? path.substring(index + 1, path.length) : path;
                    if (!node) {
                        node = this.createElementNSPlus("Layer");
                        this.writeNode("ows:Title", title, node);
                        this.nestingLayerLookup[path] = node;
                    }
                    options.subPaths.shift();
                    this.writeNode("_Layer", options, node);
                    return node;
                } else {
                    if (layer instanceof OpenLayers.Layer.WMS) {
                        node = this.writeNode("_WMS", layer);
                    } else if (layer instanceof OpenLayers.Layer.Vector) {
                        if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) {
                            node = this.writeNode("_WFS", layer);
                        } else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) {
                            if (layer.protocol.format instanceof OpenLayers.Format.GML) {
                                layer.protocol.format.version = "2.1.2";
                                node = this.writeNode("_GML", layer);
                            } else if (layer.protocol.format instanceof OpenLayers.Format.KML) {
                                layer.protocol.format.version = "2.2";
                                node = this.writeNode("_KML", layer);
                            }
                        } else {
                            this.setNamespace("feature", this.featureNS);
                            node = this.writeNode("_InlineGeometry", layer);
                        }
                    }
                    if (layer.options.maxScale) {
                        this.writeNode("sld:MinScaleDenominator", layer.options.maxScale, node);
                    }
                    if (layer.options.minScale) {
                        this.writeNode("sld:MaxScaleDenominator", layer.options.minScale, node);
                    }
                    this.nestingLayerLookup[layer.name] = node;
                    return node;
                }
            },
            "_WFS": function (layer) {
                var node = this.createElementNSPlus("Layer", {
                    attributes: {
                        name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType,
                        hidden: layer.visibility ? "0" : "1"
                    }
                });
                this.writeNode("ows:Title", layer.name, node);
                this.writeNode("Server", {
                    service: OpenLayers.Format.Context.serviceTypes.WFS,
                    version: layer.protocol.version,
                    url: layer.protocol.url
                }, node);
                return node;
            },
            "_InlineGeometry": function (layer) {
                var node = this.createElementNSPlus("Layer", {
                    attributes: {
                        name: this.featureType,
                        hidden: layer.visibility ? "0" : "1"
                    }
                });
                this.writeNode("ows:Title", layer.name, node);
                this.writeNode("InlineGeometry", layer, node);
                return node;
            },
            "_GML": function (layer) {
                var node = this.createElementNSPlus("Layer");
                this.writeNode("ows:Title", layer.name, node);
                this.writeNode("Server", {
                    service: OpenLayers.Format.Context.serviceTypes.GML,
                    url: layer.protocol.url,
                    version: layer.protocol.format.version
                }, node);
                return node;
            },
            "_KML": function (layer) {
                var node = this.createElementNSPlus("Layer");
                this.writeNode("ows:Title", layer.name, node);
                this.writeNode("Server", {
                    service: OpenLayers.Format.Context.serviceTypes.KML,
                    version: layer.protocol.format.version,
                    url: layer.protocol.url
                }, node);
                return node;
            }
        },
        "gml": OpenLayers.Util.applyDefaults({
            "boundedBy": function (bounds) {
                var node = this.createElementNSPlus("gml:boundedBy");
                this.writeNode("gml:Box", bounds, node);
                return node;
            }
        }, OpenLayers.Format.GML.v2.prototype.writers.gml),
        "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows,
        "sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld,
        "feature": OpenLayers.Format.GML.v2.prototype.writers.feature
    },
    CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1"
});
OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, {
    maxWidth: 100,
    topOutUnits: "km",
    topInUnits: "m",
    bottomOutUnits: "mi",
    bottomInUnits: "ft",
    eTop: null,
    eBottom: null,
    geodesic: false,
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!this.eTop) {
            this.eTop = document.createElement("div");
            this.eTop.className = this.displayClass + "Top";
            var theLen = this.topInUnits.length;
            this.div.appendChild(this.eTop);
            if ((this.topOutUnits == "") || (this.topInUnits == "")) {
                this.eTop.style.visibility = "hidden";
            } else {
                this.eTop.style.visibility = "visible";
            }
            this.eBottom = document.createElement("div");
            this.eBottom.className = this.displayClass + "Bottom";
            this.div.appendChild(this.eBottom);
            if ((this.bottomOutUnits == "") || (this.bottomInUnits == "")) {
                this.eBottom.style.visibility = "hidden";
            } else {
                this.eBottom.style.visibility = "visible";
            }
        }
        this.map.events.register('moveend', this, this.update);
        this.update();
        return this.div;
    },
    getBarLen: function (maxLen) {
        var digits = parseInt(Math.log(maxLen) / Math.log(10));
        var pow10 = Math.pow(10, digits);
        var firstChar = parseInt(maxLen / pow10);
        var barLen;
        if (firstChar > 5) {
            barLen = 5;
        } else if (firstChar > 2) {
            barLen = 2;
        } else {
            barLen = 1;
        }
        return barLen * pow10;
    },
    update: function () {
        var res = this.map.getResolution();
        if (!res) {
            return;
        }
        var curMapUnits = this.map.getUnits();
        var inches = OpenLayers.INCHES_PER_UNIT;
        var maxSizeData = this.maxWidth * res * inches[curMapUnits];
        var geodesicRatio = 1;
        if (this.geodesic === true) {
            var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || 0.000001) * this.maxWidth;
            var maxSizeKilometers = maxSizeData / inches["km"];
            geodesicRatio = maxSizeGeodesic / maxSizeKilometers;
            maxSizeData *= geodesicRatio;
        }
        var topUnits;
        var bottomUnits;
        if (maxSizeData > 100000) {
            topUnits = this.topOutUnits;
            bottomUnits = this.bottomOutUnits;
        } else {
            topUnits = this.topInUnits;
            bottomUnits = this.bottomInUnits;
        }
        var topMax = maxSizeData / inches[topUnits];
        var bottomMax = maxSizeData / inches[bottomUnits];
        var topRounded = this.getBarLen(topMax);
        var bottomRounded = this.getBarLen(bottomMax);
        topMax = topRounded / inches[curMapUnits] * inches[topUnits];
        bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits];
        var topPx = topMax / res / geodesicRatio;
        var bottomPx = bottomMax / res / geodesicRatio;
        if (this.eBottom.style.visibility == "visible") {
            this.eBottom.style.width = Math.round(bottomPx) + "px";
            this.eBottom.innerHTML = bottomRounded + " " + bottomUnits;
        }
        if (this.eTop.style.visibility == "visible") {
            this.eTop.style.width = Math.round(topPx) + "px";
            this.eTop.innerHTML = topRounded + " " + topUnits;
        }
    },
    CLASS_NAME: "OpenLayers.Control.ScaleLine"
});
OpenLayers.Icon = OpenLayers.Class({
    url: null,
    size: null,
    offset: null,
    calculateOffset: null,
    imageDiv: null,
    px: null,
    initialize: function (url, size, offset, calculateOffset) {
        this.url = url;
        this.size = (size) ? size : new OpenLayers.Size(20, 20);
        this.offset = offset ? offset : new OpenLayers.Pixel(-(this.size.w / 2), -(this.size.h / 2));
        this.calculateOffset = calculateOffset;
        var id = OpenLayers.Util.createUniqueID("OL_Icon_");
        this.imageDiv = OpenLayers.Util.createAlphaImageDiv(id);
    },
    destroy: function () {
        this.erase();
        OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild);
        this.imageDiv.innerHTML = "";
        this.imageDiv = null;
    },
    clone: function () {
        return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset);
    },
    setSize: function (size) {
        if (size != null) {
            this.size = size;
        }
        this.draw();
    },
    setUrl: function (url) {
        if (url != null) {
            this.url = url;
        }
        this.draw();
    },
    draw: function (px) {
        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, "absolute");
        this.moveTo(px);
        return this.imageDiv;
    },
    erase: function () {
        if (this.imageDiv != null && this.imageDiv.parentNode != null) {
            OpenLayers.Element.remove(this.imageDiv);
        }
    },
    setOpacity: function (opacity) {
        OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity);
    },
    moveTo: function (px) {
        if (px != null) {
            this.px = px;
        }
        if (this.imageDiv != null) {
            if (this.px == null) {
                this.display(false);
            } else {
                if (this.calculateOffset) {
                    this.offset = this.calculateOffset(this.size);
                }
                var offsetPx = this.px.offset(this.offset);
                OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, offsetPx);
            }
        }
    },
    display: function (display) {
        this.imageDiv.style.display = (display) ? "" : "none";
    },
    isDrawn: function () {
        var isDrawn = (this.imageDiv && this.imageDiv.parentNode && (this.imageDiv.parentNode.nodeType != 11));
        return isDrawn;
    },
    CLASS_NAME: "OpenLayers.Icon"
});
OpenLayers.Marker = OpenLayers.Class({
    icon: null,
    lonlat: null,
    events: null,
    map: null,
    initialize: function (lonlat, icon) {
        this.lonlat = lonlat;
        var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon();
        if (this.icon == null) {
            this.icon = newIcon;
        } else {
            this.icon.url = newIcon.url;
            this.icon.size = newIcon.size;
            this.icon.offset = newIcon.offset;
            this.icon.calculateOffset = newIcon.calculateOffset;
        }
        this.events = new OpenLayers.Events(this, this.icon.imageDiv, null);
    },
    destroy: function () {
        this.erase();
        this.map = null;
        this.events.destroy();
        this.events = null;
        if (this.icon != null) {
            this.icon.destroy();
            this.icon = null;
        }
    },
    draw: function (px) {
        return this.icon.draw(px);
    },
    erase: function () {
        if (this.icon != null) {
            this.icon.erase();
        }
    },
    moveTo: function (px) {
        if ((px != null) && (this.icon != null)) {
            this.icon.moveTo(px);
        }
        this.lonlat = this.map.getLonLatFromLayerPx(px);
    },
    isDrawn: function () {
        var isDrawn = (this.icon && this.icon.isDrawn());
        return isDrawn;
    },
    onScreen: function () {
        var onScreen = false;
        if (this.map) {
            var screenBounds = this.map.getExtent();
            onScreen = screenBounds.containsLonLat(this.lonlat);
        }
        return onScreen;
    },
    inflate: function (inflate) {
        if (this.icon) {
            var newSize = new OpenLayers.Size(this.icon.size.w * inflate, this.icon.size.h * inflate);
            this.icon.setSize(newSize);
        }
    },
    setOpacity: function (opacity) {
        this.icon.setOpacity(opacity);
    },
    setUrl: function (url) {
        this.icon.setUrl(url);
    },
    display: function (display) {
        this.icon.display(display);
    },
    CLASS_NAME: "OpenLayers.Marker"
});
OpenLayers.Marker.defaultIcon = function () {
    var url = OpenLayers.Util.getImagesLocation() + "marker.png";
    var size = new OpenLayers.Size(21, 25);
    var calculateOffset = function (size) {
            return new OpenLayers.Pixel(-(size.w / 2), -size.h);
        };
    return new OpenLayers.Icon(url, size, null, calculateOffset);
};
OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    format: 'image/png',
    serverResolutions: null,
    initialize: function (name, url, layername, options) {
        this.layername = layername;
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url,
        {},
        options]);
        this.extension = this.format.split('/')[1].toLowerCase();
        this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension;
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.TileCache(this.name, this.url, this.layername, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        var res = this.map.getResolution();
        var bbox = this.maxExtent;
        var size = this.tileSize;
        var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));
        var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h));
        var tileZ = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom();

        function zeroPad(number, length) {
            number = String(number);
            var zeros = [];
            for (var i = 0; i < length; ++i) {
                zeros.push('0');
            }
            return zeros.join('').substring(0, length - number.length) + number;
        }
        var components = [this.layername, zeroPad(tileZ, 2), zeroPad(parseInt(tileX / 1000000), 3), zeroPad((parseInt(tileX / 1000) % 1000), 3), zeroPad((parseInt(tileX) % 1000), 3), zeroPad(parseInt(tileY / 1000000), 3), zeroPad((parseInt(tileY / 1000) % 1000), 3), zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension];
        var path = components.join('/');
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(path, url);
        }
        url = (url.charAt(url.length - 1) == '/') ? url : url + '/';
        return url + path;
    },
    CLASS_NAME: "OpenLayers.Layer.TileCache"
});
OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, {
    features: null,
    length: 10,
    num: null,
    paging: false,
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.call(this);
        if (activated) {
            this.layer.events.on({
                "beforefeaturesadded": this.cacheFeatures,
                scope: this
            });
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            this.clearCache();
            this.layer.events.un({
                "beforefeaturesadded": this.cacheFeatures,
                scope: this
            });
        }
        return deactivated;
    },
    cacheFeatures: function (event) {
        if (!this.paging) {
            this.clearCache();
            this.features = event.features;
            this.pageNext(event);
        }
    },
    clearCache: function () {
        if (this.features) {
            for (var i = 0; i < this.features.length; ++i) {
                this.features[i].destroy();
            }
        }
        this.features = null;
        this.num = null;
    },
    pageCount: function () {
        var numFeatures = this.features ? this.features.length : 0;
        return Math.ceil(numFeatures / this.length);
    },
    pageNum: function () {
        return this.num;
    },
    pageLength: function (newLength) {
        if (newLength && newLength > 0) {
            this.length = newLength;
        }
        return this.length;
    },
    pageNext: function (event) {
        var changed = false;
        if (this.features) {
            if (this.num === null) {
                this.num = -1;
            }
            var start = (this.num + 1) * this.length;
            changed = this.page(start, event);
        }
        return changed;
    },
    pagePrevious: function () {
        var changed = false;
        if (this.features) {
            if (this.num === null) {
                this.num = this.pageCount();
            }
            var start = (this.num - 1) * this.length;
            changed = this.page(start);
        }
        return changed;
    },
    page: function (start, event) {
        var changed = false;
        if (this.features) {
            if (start >= 0 && start < this.features.length) {
                var num = Math.floor(start / this.length);
                if (num != this.num) {
                    this.paging = true;
                    var features = this.features.slice(start, start + this.length);
                    this.layer.removeFeatures(this.layer.features);
                    this.num = num;
                    if (event && event.features) {
                        event.features = features;
                    } else {
                        this.layer.addFeatures(features);
                    }
                    this.paging = false;
                    changed = true;
                }
            }
        }
        return changed;
    },
    CLASS_NAME: "OpenLayers.Strategy.Paging"
});
OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["beforesetfeature", "setfeature", "beforetransform", "transform", "transformcomplete"],
    geometryTypes: null,
    layer: null,
    preserveAspectRatio: false,
    rotate: true,
    feature: null,
    renderIntent: "temporary",
    rotationHandleSymbolizer: null,
    box: null,
    center: null,
    scale: 1,
    ratio: 1,
    rotation: 0,
    handles: null,
    rotationHandles: null,
    dragControl: null,
    initialize: function (layer, options) {
        this.EVENT_TYPES = OpenLayers.Control.TransformFeature.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        if (!this.rotationHandleSymbolizer) {
            this.rotationHandleSymbolizer = {
                stroke: false,
                pointRadius: 10,
                fillOpacity: 0,
                cursor: "pointer"
            };
        }
        this.createBox();
        this.createControl();
    },
    activate: function () {
        var activated = false;
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            this.dragControl.activate();
            this.layer.addFeatures([this.box]);
            this.rotate && this.layer.addFeatures(this.rotationHandles);
            this.layer.addFeatures(this.handles);
            activated = true;
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.layer.removeFeatures(this.handles);
            this.rotate && this.layer.removeFeatures(this.rotationHandles);
            this.layer.removeFeatures([this.box]);
            this.dragControl.deactivate();
            deactivated = true;
        }
        if (deactivated) {
            this.unsetFeature();
        }
        return deactivated;
    },
    setMap: function (map) {
        this.dragControl.setMap(map);
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    setFeature: function (feature, initialParams) {
        initialParams = OpenLayers.Util.applyDefaults(initialParams, {
            rotation: 0,
            scale: 1,
            ratio: 1
        });
        var oldRotation = this.rotation;
        var oldCenter = this.center;
        OpenLayers.Util.extend(this, initialParams);
        var cont = this.events.triggerEvent("beforesetfeature", {
            feature: feature
        });
        if (cont === false) {
            return;
        }
        this.feature = feature;
        this.activate();
        this._setfeature = true;
        var featureBounds = this.feature.geometry.getBounds();
        this.box.move(featureBounds.getCenterLonLat());
        this.box.geometry.rotate(-oldRotation, oldCenter);
        this._angle = 0;
        var ll;
        if (this.rotation) {
            var geom = feature.geometry.clone();
            geom.rotate(-this.rotation, this.center);
            var box = new OpenLayers.Feature.Vector(geom.getBounds().toGeometry());
            box.geometry.rotate(this.rotation, this.center);
            this.box.geometry.rotate(this.rotation, this.center);
            this.box.move(box.geometry.getBounds().getCenterLonLat());
            var llGeom = box.geometry.components[0].components[0];
            ll = llGeom.getBounds().getCenterLonLat();
        } else {
            ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom);
        }
        this.handles[0].move(ll);
        delete this._setfeature;
        this.events.triggerEvent("setfeature", {
            feature: feature
        });
    },
    unsetFeature: function () {
        if (this.active) {
            this.deactivate();
        } else {
            this.feature = null;
            this.rotation = 0;
            this.scale = 1;
            this.ratio = 1;
        }
    },
    createBox: function () {
        var control = this;
        this.center = new OpenLayers.Geometry.Point(0, 0);
        var box = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-1, -1), new OpenLayers.Geometry.Point(0, -1), new OpenLayers.Geometry.Point(1, -1), new OpenLayers.Geometry.Point(1, 0), new OpenLayers.Geometry.Point(1, 1), new OpenLayers.Geometry.Point(0, 1), new OpenLayers.Geometry.Point(-1, 1), new OpenLayers.Geometry.Point(-1, 0), new OpenLayers.Geometry.Point(-1, -1)]), null, typeof this.renderIntent == "string" ? null : this.renderIntent);
        box.geometry.move = function (x, y) {
            control._moving = true;
            OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments);
            control.center.move(x, y);
            delete control._moving;
        };
        var vertexMoveFn = function (x, y) {
                OpenLayers.Geometry.Point.prototype.move.apply(this, arguments);
                this._rotationHandle && this._rotationHandle.geometry.move(x, y);
                this._handle.geometry.move(x, y);
            };
        var vertexResizeFn = function (scale, center, ratio) {
                OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments);
                this._rotationHandle && this._rotationHandle.geometry.resize(scale, center, ratio);
                this._handle.geometry.resize(scale, center, ratio);
            };
        var vertexRotateFn = function (angle, center) {
                OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments);
                this._rotationHandle && this._rotationHandle.geometry.rotate(angle, center);
                this._handle.geometry.rotate(angle, center);
            };
        var handleMoveFn = function (x, y) {
                var oldX = this.x,
                    oldY = this.y;
                OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
                if (control._moving) {
                    return;
                }
                var evt = control.dragControl.handlers.drag.evt;
                var preserveAspectRatio = !control._setfeature && control.preserveAspectRatio;
                var reshape = !preserveAspectRatio && !(evt && evt.shiftKey);
                var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY);
                var centerGeometry = control.center;
                this.rotate(-control.rotation, centerGeometry);
                oldGeom.rotate(-control.rotation, centerGeometry);
                var dx1 = this.x - centerGeometry.x;
                var dy1 = this.y - centerGeometry.y;
                var dx0 = dx1 - (this.x - oldGeom.x);
                var dy0 = dy1 - (this.y - oldGeom.y);
                this.x = oldX;
                this.y = oldY;
                var scale, ratio = 1;
                if (reshape) {
                    scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0;
                    ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale;
                } else {
                    var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0));
                    var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1));
                    scale = l1 / l0;
                }
                control._moving = true;
                control.box.geometry.rotate(-control.rotation, centerGeometry);
                delete control._moving;
                control.box.geometry.resize(scale, centerGeometry, ratio);
                control.box.geometry.rotate(control.rotation, centerGeometry);
                control.transformFeature({
                    scale: scale,
                    ratio: ratio
                });
            };
        var rotationHandleMoveFn = function (x, y) {
                var oldX = this.x,
                    oldY = this.y;
                OpenLayers.Geometry.Point.prototype.move.call(this, x, y);
                if (control._moving) {
                    return;
                }
                var evt = control.dragControl.handlers.drag.evt;
                var constrain = (evt && evt.shiftKey) ? 45 : 1;
                var centerGeometry = control.center;
                var dx1 = this.x - centerGeometry.x;
                var dy1 = this.y - centerGeometry.y;
                var dx0 = dx1 - x;
                var dy0 = dy1 - y;
                this.x = oldX;
                this.y = oldY;
                var a0 = Math.atan2(dy0, dx0);
                var a1 = Math.atan2(dy1, dx1);
                var angle = a1 - a0;
                angle *= 180 / Math.PI;
                control._angle = (control._angle + angle) % 360;
                var diff = control.rotation % constrain;
                if (Math.abs(control._angle) >= constrain || diff !== 0) {
                    angle = Math.round(control._angle / constrain) * constrain - diff;
                    control._angle = 0;
                    control.box.geometry.rotate(angle, centerGeometry);
                    control.transformFeature({
                        rotation: angle
                    });
                }
            };
        var handles = new Array(8);
        var rotationHandles = new Array(4);
        var geom, handle, rotationHandle;
        for (var i = 0; i < 8; ++i) {
            geom = box.geometry.components[i];
            handle = new OpenLayers.Feature.Vector(geom.clone(), null, typeof this.renderIntent == "string" ? null : this.renderIntent);
            if (i % 2 == 0) {
                rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), null, typeof this.rotationHandleSymbolizer == "string" ? null : this.rotationHandleSymbolizer);
                rotationHandle.geometry.move = rotationHandleMoveFn;
                geom._rotationHandle = rotationHandle;
                rotationHandles[i / 2] = rotationHandle;
            }
            geom.move = vertexMoveFn;
            geom.resize = vertexResizeFn;
            geom.rotate = vertexRotateFn;
            handle.geometry.move = handleMoveFn;
            geom._handle = handle;
            handles[i] = handle;
        }
        this.box = box;
        this.rotationHandles = rotationHandles;
        this.handles = handles;
    },
    createControl: function () {
        var control = this;
        this.dragControl = new OpenLayers.Control.DragFeature(this.layer, {
            documentDrag: true,
            moveFeature: function (pixel) {
                if (this.feature === control.feature) {
                    this.feature = control.box;
                }
                OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments);
            },
            onDrag: function (feature, pixel) {
                if (feature === control.box) {
                    control.transformFeature({
                        center: control.center
                    });
                    control.drawHandles();
                }
            },
            onStart: function (feature, pixel) {
                var eligible = !control.geometryTypes || OpenLayers.Util.indexOf(control.geometryTypes, feature.geometry.CLASS_NAME) !== -1;
                var i = OpenLayers.Util.indexOf(control.handles, feature);
                i += OpenLayers.Util.indexOf(control.rotationHandles, feature);
                if (feature !== control.feature && feature !== control.box && i == -2 && eligible) {
                    control.setFeature(feature);
                }
            },
            onComplete: function (feature, pixel) {
                control.events.triggerEvent("transformcomplete", {
                    feature: control.feature
                });
            }
        });
    },
    drawHandles: function () {
        var layer = this.layer;
        for (var i = 0; i < 8; ++i) {
            if (this.rotate && i % 2 === 0) {
                layer.drawFeature(this.rotationHandles[i / 2], this.rotationHandleSymbolizer);
            }
            layer.drawFeature(this.handles[i], this.renderIntent);
        }
    },
    transformFeature: function (mods) {
        if (!this._setfeature) {
            this.scale *= (mods.scale || 1);
            this.ratio *= (mods.ratio || 1);
            var oldRotation = this.rotation;
            this.rotation = (this.rotation + (mods.rotation || 0)) % 360;
            if (this.events.triggerEvent("beforetransform", mods) !== false) {
                var feature = this.feature;
                var geom = feature.geometry;
                var center = this.center;
                geom.rotate(-oldRotation, center);
                if (mods.scale || mods.ratio) {
                    geom.resize(mods.scale, center, mods.ratio);
                } else if (mods.center) {
                    feature.move(mods.center.getBounds().getCenterLonLat());
                }
                geom.rotate(this.rotation, center);
                this.layer.drawFeature(feature);
                feature.toState(OpenLayers.State.UPDATE);
                this.events.triggerEvent("transform", mods);
            }
        }
        this.layer.drawFeature(this.box, this.renderIntent);
        this.drawHandles();
    },
    destroy: function () {
        var geom;
        for (var i = 0; i < 8; ++i) {
            geom = this.box.geometry.components[i];
            geom._handle.destroy();
            geom._handle = null;
            geom._rotationHandle && geom._rotationHandle.destroy();
            geom._rotationHandle = null;
        }
        this.box.destroy();
        this.box = null;
        this.layer = null;
        this.dragControl.destroy();
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.TransformFeature"
});
OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    sphericalMercator: false,
    zoomOffset: 0,
    serverResolutions: null,
    initialize: function (name, url, options) {
        if (options && options.sphericalMercator || this.sphericalMercator) {
            options = OpenLayers.Util.extend({
                maxExtent: new OpenLayers.Bounds(-128 * 156543.03390625, -128 * 156543.03390625, 128 * 156543.03390625, 128 * 156543.03390625),
                maxResolution: 156543.03390625,
                numZoomLevels: 19,
                units: "m",
                projection: "EPSG:900913"
            }, options);
        }
        url = url || this.url;
        name = name || this.name;
        var newArguments = [name, url,
        {},
        options];
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        var xyz = this.getXYZ(bounds);
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            var s = '' + xyz.x + xyz.y + xyz.z;
            url = this.selectUrl(s, url);
        }
        return OpenLayers.String.format(url, xyz);
    },
    getXYZ: function (bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom() + this.zoomOffset;
        var limit = Math.pow(2, z);
        if (this.wrapDateLine) {
            x = ((x % limit) + limit) % limit;
        }
        return {
            'x': x,
            'y': y,
            'z': z
        };
    },
    setMap: function (map) {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        if (!this.tileOrigin) {
            this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.XYZ"
});
OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, {
    name: "OpenStreetMap",
    attribution: "Data CC-By-SA by <a href='http://openstreetmap.org/'>OpenStreetMap</a>",
    sphericalMercator: true,
    url: 'http://tile.openstreetmap.org/${z}/${x}/${y}.png',
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions());
        }
        obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
        return obj;
    },
    wrapDateLine: true,
    CLASS_NAME: "OpenLayers.Layer.OSM"
});
OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, {
    dragHandler: null,
    boxDivClassName: 'olHandlerBoxZoomBox',
    boxOffsets: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
        this.dragHandler = new OpenLayers.Handler.Drag(this, {
            down: this.startBox,
            move: this.moveBox,
            out: this.removeBox,
            up: this.endBox
        }, {
            keyMask: this.keyMask
        });
    },
    destroy: function () {
        OpenLayers.Handler.prototype.destroy.apply(this, arguments);
        if (this.dragHandler) {
            this.dragHandler.destroy();
            this.dragHandler = null;
        }
    },
    setMap: function (map) {
        OpenLayers.Handler.prototype.setMap.apply(this, arguments);
        if (this.dragHandler) {
            this.dragHandler.setMap(map);
        }
    },
    startBox: function (xy) {
        this.callback("start", []);
        this.zoomBox = OpenLayers.Util.createDiv('zoomBox', new OpenLayers.Pixel(-9999, -9999));
        this.zoomBox.className = this.boxDivClassName;
        this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
        this.map.eventsDiv.appendChild(this.zoomBox);
        OpenLayers.Element.addClass(this.map.eventsDiv, "olDrawBox");
    },
    moveBox: function (xy) {
        var startX = this.dragHandler.start.x;
        var startY = this.dragHandler.start.y;
        var deltaX = Math.abs(startX - xy.x);
        var deltaY = Math.abs(startY - xy.y);
        var offset = this.getBoxOffsets();
        this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
        this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
        this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px";
        this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px";
    },
    endBox: function (end) {
        var result;
        if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) {
            var start = this.dragHandler.start;
            var top = Math.min(start.y, end.y);
            var bottom = Math.max(start.y, end.y);
            var left = Math.min(start.x, end.x);
            var right = Math.max(start.x, end.x);
            result = new OpenLayers.Bounds(left, bottom, right, top);
        } else {
            result = this.dragHandler.start.clone();
        }
        this.removeBox();
        this.callback("done", [result]);
    },
    removeBox: function () {
        this.map.eventsDiv.removeChild(this.zoomBox);
        this.zoomBox = null;
        this.boxOffsets = null;
        OpenLayers.Element.removeClass(this.map.eventsDiv, "olDrawBox");
    },
    activate: function () {
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.dragHandler.activate();
            return true;
        } else {
            return false;
        }
    },
    deactivate: function () {
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            if (this.dragHandler.deactivate()) {
                if (this.zoomBox) {
                    this.removeBox();
                }
            }
            return true;
        } else {
            return false;
        }
    },
    getBoxOffsets: function () {
        if (!this.boxOffsets) {
            var testDiv = document.createElement("div");
            testDiv.style.position = "absolute";
            testDiv.style.border = "1px solid black";
            testDiv.style.width = "3px";
            document.body.appendChild(testDiv);
            var w3cBoxModel = testDiv.clientWidth == 3;
            document.body.removeChild(testDiv);
            var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width"));
            var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-right-width"));
            var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width"));
            var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-bottom-width"));
            this.boxOffsets = {
                left: left,
                right: right,
                top: top,
                bottom: bottom,
                width: w3cBoxModel === false ? left + right : 0,
                height: w3cBoxModel === false ? top + bottom : 0
            };
        }
        return this.boxOffsets;
    },
    CLASS_NAME: "OpenLayers.Handler.Box"
});
OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_TOOL,
    out: false,
    alwaysZoom: false,
    draw: function () {
        this.handler = new OpenLayers.Handler.Box(this, {
            done: this.zoomBox
        }, {
            keyMask: this.keyMask
        });
    },
    zoomBox: function (position) {
        if (position instanceof OpenLayers.Bounds) {
            var bounds;
            if (!this.out) {
                var minXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.left, position.bottom));
                var maxXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.right, position.top));
                bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);
            } else {
                var pixWidth = Math.abs(position.right - position.left);
                var pixHeight = Math.abs(position.top - position.bottom);
                var zoomFactor = Math.min((this.map.size.h / pixHeight), (this.map.size.w / pixWidth));
                var extent = this.map.getExtent();
                var center = this.map.getLonLatFromPixel(position.getCenterPixel());
                var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor;
                var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor;
                var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor;
                var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor;
                bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax);
            }
            var lastZoom = this.map.getZoom();
            this.map.zoomToExtent(bounds);
            if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) {
                this.map.zoomTo(lastZoom + (this.out ? -1 : 1));
            }
        } else {
            if (!this.out) {
                this.map.setCenter(this.map.getLonLatFromPixel(position), this.map.getZoom() + 1);
            } else {
                this.map.setCenter(this.map.getLonLatFromPixel(position), this.map.getZoom() - 1);
            }
        }
    },
    CLASS_NAME: "OpenLayers.Control.ZoomBox"
});
OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_TOOL,
    panned: false,
    interval: 1,
    documentDrag: false,
    kinetic: null,
    enableKinetic: false,
    kineticInterval: 10,
    draw: function () {
        if (this.enableKinetic) {
            var config = {
                interval: this.kineticInterval
            };
            if (typeof this.enableKinetic === "object") {
                config = OpenLayers.Util.extend(config, this.enableKinetic);
            }
            this.kinetic = new OpenLayers.Kinetic(config);
        }
        this.handler = new OpenLayers.Handler.Drag(this, {
            "move": this.panMap,
            "done": this.panMapDone,
            "down": this.panMapStart
        }, {
            interval: this.interval,
            documentDrag: this.documentDrag
        });
    },
    panMapStart: function () {
        if (this.kinetic) {
            this.kinetic.begin();
        }
    },
    panMap: function (xy) {
        if (this.kinetic) {
            this.kinetic.update(xy);
        }
        this.panned = true;
        this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {
            dragging: true,
            animate: false
        });
    },
    panMapDone: function (xy) {
        if (this.panned) {
            var res = null;
            if (this.kinetic) {
                res = this.kinetic.end(xy);
            }
            this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, {
                dragging: !! res,
                animate: false
            });
            if (res) {
                var self = this;
                this.kinetic.move(res, function (x, y, end) {
                    self.map.pan(x, y, {
                        dragging: !end,
                        animate: false
                    });
                });
            }
            this.panned = false;
        }
    },
    CLASS_NAME: "OpenLayers.Control.DragPan"
});
OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, {
    delay: 300,
    single: true,
    'double': false,
    pixelTolerance: 0,
    dblclickTolerance: 13,
    stopSingle: false,
    stopDouble: false,
    timerId: null,
    touch: false,
    down: null,
    last: null,
    first: null,
    rightclickTimerId: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },
    touchstart: function (evt) {
        if (!this.touch) {
            this.unregisterMouseListeners();
            this.touch = true;
        }
        this.down = this.getEventInfo(evt);
        this.last = this.getEventInfo(evt);
        return true;
    },
    touchmove: function (evt) {
        this.last = this.getEventInfo(evt);
        return true;
    },
    touchend: function (evt) {
        if (this.down) {
            evt.xy = this.last.xy;
            evt.lastTouches = this.last.touches;
            this.handleSingle(evt);
            this.down = null;
        }
        return true;
    },
    unregisterMouseListeners: function () {
        this.map.events.un({
            mousedown: this.mousedown,
            mouseup: this.mouseup,
            click: this.click,
            dblclick: this.dblclick,
            scope: this
        });
    },
    mousedown: function (evt) {
        this.down = this.getEventInfo(evt);
        this.last = this.getEventInfo(evt);
        return true;
    },
    mouseup: function (evt) {
        var propagate = true;
        if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) {
            propagate = this.rightclick(evt);
        }
        return propagate;
    },
    rightclick: function (evt) {
        if (this.passesTolerance(evt)) {
            if (this.rightclickTimerId != null) {
                this.clearTimer();
                this.callback('dblrightclick', [evt]);
                return !this.stopDouble;
            } else {
                var clickEvent = this['double'] ? OpenLayers.Util.extend({}, evt) : this.callback('rightclick', [evt]);
                var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent);
                this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay);
            }
        }
        return !this.stopSingle;
    },
    delayedRightCall: function (evt) {
        this.rightclickTimerId = null;
        if (evt) {
            this.callback('rightclick', [evt]);
        }
    },
    click: function (evt) {
        if (!this.last) {
            this.last = this.getEventInfo(evt);
        }
        this.handleSingle(evt);
        return !this.stopSingle;
    },
    dblclick: function (evt) {
        this.handleDouble(evt);
        return !this.stopDouble;
    },
    handleDouble: function (evt) {
        if (this["double"] && this.passesDblclickTolerance(evt)) {
            this.callback("dblclick", [evt]);
        }
    },
    handleSingle: function (evt) {
        if (this.passesTolerance(evt)) {
            if (this.timerId != null) {
                if (this.last.touches && this.last.touches.length === 1) {
                    if (this["double"]) {
                        OpenLayers.Event.stop(evt);
                    }
                    this.handleDouble(evt);
                }
                if (!this.last.touches || this.last.touches.length !== 2) {
                    this.clearTimer();
                }
            } else {
                this.first = this.getEventInfo(evt);
                var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null;
                this.queuePotentialClick(clickEvent);
            }
        }
    },
    queuePotentialClick: function (evt) {
        this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay);
    },
    passesTolerance: function (evt) {
        var passes = true;
        if (this.pixelTolerance != null && this.down && this.down.xy) {
            passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
            if (passes && this.touch && this.down.touches.length === this.last.touches.length) {
                for (var i = 0, ii = this.down.touches.length; i < ii; ++i) {
                    if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) {
                        passes = false;
                        break;
                    }
                }
            }
        }
        return passes;
    },
    getTouchDistance: function (from, to) {
        return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2));
    },
    passesDblclickTolerance: function (evt) {
        var passes = true;
        if (this.down && this.first) {
            passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
        }
        return passes;
    },
    clearTimer: function () {
        if (this.timerId != null) {
            window.clearTimeout(this.timerId);
            this.timerId = null;
        }
        if (this.rightclickTimerId != null) {
            window.clearTimeout(this.rightclickTimerId);
            this.rightclickTimerId = null;
        }
    },
    delayedCall: function (evt) {
        this.timerId = null;
        if (evt) {
            this.callback("click", [evt]);
        }
    },
    getEventInfo: function (evt) {
        var touches;
        if (evt.touches) {
            var len = evt.touches.length;
            touches = new Array(len);
            var touch;
            for (var i = 0; i < len; i++) {
                touch = evt.touches[i];
                touches[i] = {
                    clientX: touch.clientX,
                    clientY: touch.clientY
                };
            }
        }
        return {
            xy: evt.xy,
            touches: touches
        };
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.clearTimer();
            this.down = null;
            this.first = null;
            this.last = null;
            this.touch = false;
            deactivated = true;
        }
        return deactivated;
    },
    CLASS_NAME: "OpenLayers.Handler.Click"
});
OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, {
    dragPan: null,
    dragPanOptions: null,
    pinchZoom: null,
    pinchZoomOptions: null,
    documentDrag: false,
    zoomBox: null,
    zoomBoxEnabled: true,
    zoomWheelEnabled: true,
    mouseWheelOptions: null,
    handleRightClicks: false,
    zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT,
    autoActivate: true,
    initialize: function (options) {
        this.handlers = {};
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },
    destroy: function () {
        this.deactivate();
        if (this.dragPan) {
            this.dragPan.destroy();
        }
        this.dragPan = null;
        if (this.zoomBox) {
            this.zoomBox.destroy();
        }
        this.zoomBox = null;
        if (this.pinchZoom) {
            this.pinchZoom.destroy();
        }
        this.pinchZoom = null;
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    activate: function () {
        this.dragPan.activate();
        if (this.zoomWheelEnabled) {
            this.handlers.wheel.activate();
        }
        this.handlers.click.activate();
        if (this.zoomBoxEnabled) {
            this.zoomBox.activate();
        }
        if (this.pinchZoom) {
            this.pinchZoom.activate();
        }
        return OpenLayers.Control.prototype.activate.apply(this, arguments);
    },
    deactivate: function () {
        if (this.pinchZoom) {
            this.pinchZoom.deactivate();
        }
        this.zoomBox.deactivate();
        this.dragPan.deactivate();
        this.handlers.click.deactivate();
        this.handlers.wheel.deactivate();
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    draw: function () {
        if (this.handleRightClicks) {
            this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False;
        }
        var clickCallbacks = {
            'click': this.defaultClick,
            'dblclick': this.defaultDblClick,
            'dblrightclick': this.defaultDblRightClick
        };
        var clickOptions = {
            'double': true,
            'stopDouble': true
        };
        this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);
        this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({
            map: this.map,
            documentDrag: this.documentDrag
        }, this.dragPanOptions));
        this.zoomBox = new OpenLayers.Control.ZoomBox({
            map: this.map,
            keyMask: this.zoomBoxKeyMask
        });
        this.dragPan.draw();
        this.zoomBox.draw();
        this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, {
            "up": this.wheelUp,
            "down": this.wheelDown
        }, this.mouseWheelOptions);
        if (OpenLayers.Control.PinchZoom) {
            this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({
                map: this.map
            }, this.pinchZoomOptions));
        }
    },
    defaultClick: function (evt) {
        if (evt.lastTouches && evt.lastTouches.length == 2) {
            this.map.zoomOut();
        }
    },
    defaultDblClick: function (evt) {
        var newCenter = this.map.getLonLatFromViewPortPx(evt.xy);
        this.map.setCenter(newCenter, this.map.zoom + 1);
    },
    defaultDblRightClick: function (evt) {
        var newCenter = this.map.getLonLatFromViewPortPx(evt.xy);
        this.map.setCenter(newCenter, this.map.zoom - 1);
    },
    wheelChange: function (evt, deltaZ) {
        var currentZoom = this.map.getZoom();
        var newZoom = this.map.getZoom() + Math.round(deltaZ);
        newZoom = Math.max(newZoom, 0);
        newZoom = Math.min(newZoom, this.map.getNumZoomLevels());
        if (newZoom === currentZoom) {
            return;
        }
        var size = this.map.getSize();
        var deltaX = size.w / 2 - evt.xy.x;
        var deltaY = evt.xy.y - size.h / 2;
        var newRes = this.map.baseLayer.getResolutionForZoom(newZoom);
        var zoomPoint = this.map.getLonLatFromPixel(evt.xy);
        var newCenter = new OpenLayers.LonLat(zoomPoint.lon + deltaX * newRes, zoomPoint.lat + deltaY * newRes);
        this.map.setCenter(newCenter, newZoom);
    },
    wheelUp: function (evt, delta) {
        this.wheelChange(evt, delta || 1);
    },
    wheelDown: function (evt, delta) {
        this.wheelChange(evt, delta || -1);
    },
    disableZoomBox: function () {
        this.zoomBoxEnabled = false;
        this.zoomBox.deactivate();
    },
    enableZoomBox: function () {
        this.zoomBoxEnabled = true;
        if (this.active) {
            this.zoomBox.activate();
        }
    },
    disableZoomWheel: function () {
        this.zoomWheelEnabled = false;
        this.handlers.wheel.deactivate();
    },
    enableZoomWheel: function () {
        this.zoomWheelEnabled = true;
        if (this.active) {
            this.handlers.wheel.activate();
        }
    },
    CLASS_NAME: "OpenLayers.Control.Navigation"
});
OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, {
    layer: null,
    callbacks: null,
    EVENT_TYPES: ["featureadded"],
    multi: false,
    featureAdded: function () {},
    handlerOptions: null,
    initialize: function (layer, handler, options) {
        this.EVENT_TYPES = OpenLayers.Control.DrawFeature.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.callbacks = OpenLayers.Util.extend({
            done: this.drawFeature,
            modify: function (vertex, feature) {
                this.layer.events.triggerEvent("sketchmodified", {
                    vertex: vertex,
                    feature: feature
                });
            },
            create: function (vertex, feature) {
                this.layer.events.triggerEvent("sketchstarted", {
                    vertex: vertex,
                    feature: feature
                });
            }
        }, this.callbacks);
        this.layer = layer;
        this.handlerOptions = this.handlerOptions || {};
        if (!("multi" in this.handlerOptions)) {
            this.handlerOptions.multi = this.multi;
        }
        var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary;
        if (sketchStyle) {
            this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {
                styleMap: new OpenLayers.StyleMap({
                    "default": sketchStyle
                })
            });
        }
        this.handler = new handler(this, this.callbacks, this.handlerOptions);
    },
    drawFeature: function (geometry) {
        var feature = new OpenLayers.Feature.Vector(geometry);
        var proceed = this.layer.events.triggerEvent("sketchcomplete", {
            feature: feature
        });
        if (proceed !== false) {
            feature.state = OpenLayers.State.INSERT;
            this.layer.addFeatures([feature]);
            this.featureAdded(feature);
            this.events.triggerEvent("featureadded", {
                feature: feature
            });
        }
    },
    insertXY: function (x, y) {
        if (this.handler && this.handler.line) {
            this.handler.insertXY(x, y);
        }
    },
    insertDeltaXY: function (dx, dy) {
        if (this.handler && this.handler.line) {
            this.handler.insertDeltaXY(dx, dy);
        }
    },
    insertDirectionLength: function (direction, length) {
        if (this.handler && this.handler.line) {
            this.handler.insertDirectionLength(direction, length);
        }
    },
    insertDeflectionLength: function (deflection, length) {
        if (this.handler && this.handler.line) {
            this.handler.insertDeflectionLength(deflection, length);
        }
    },
    undo: function () {
        return this.handler.undo && this.handler.undo();
    },
    redo: function () {
        return this.handler.redo && this.handler.redo();
    },
    finishSketch: function () {
        this.handler.finishGeometry();
    },
    cancel: function () {
        this.handler.cancel();
    },
    CLASS_NAME: "OpenLayers.Control.DrawFeature"
});
OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, {
    holeModifier: null,
    drawingHole: false,
    polygon: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.Path.prototype.initialize.apply(this, arguments);
    },
    createFeature: function (pixel) {
        var lonlat = this.map.getLonLatFromPixel(pixel);
        var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
        this.point = new OpenLayers.Feature.Vector(geometry);
        this.line = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LinearRing([this.point.geometry]));
        this.polygon = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([this.line.geometry]));
        this.callback("create", [this.point.geometry, this.getSketch()]);
        this.point.geometry.clearBounds();
        this.layer.addFeatures([this.polygon, this.point], {
            silent: true
        });
    },
    addPoint: function (pixel) {
        if (!this.drawingHole && this.holeModifier && this.evt && this.evt[this.holeModifier]) {
            var geometry = this.point.geometry;
            var features = this.control.layer.features;
            var candidate, polygon;
            for (var i = features.length - 1; i >= 0; --i) {
                candidate = features[i].geometry;
                if ((candidate instanceof OpenLayers.Geometry.Polygon || candidate instanceof OpenLayers.Geometry.MultiPolygon) && candidate.intersects(geometry)) {
                    polygon = features[i];
                    this.control.layer.removeFeatures([polygon], {
                        silent: true
                    });
                    this.control.layer.events.registerPriority("sketchcomplete", this, this.finalizeInteriorRing);
                    this.control.layer.events.registerPriority("sketchmodified", this, this.enforceTopology);
                    polygon.geometry.addComponent(this.line.geometry);
                    this.polygon = polygon;
                    this.drawingHole = true;
                    break;
                }
            }
        }
        OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments);
    },
    getCurrentPointIndex: function () {
        return this.line.geometry.components.length - 2;
    },
    enforceTopology: function (event) {
        var point = event.vertex;
        var components = this.line.geometry.components;
        if (!this.polygon.geometry.intersects(point)) {
            var last = components[components.length - 3];
            point.x = last.x;
            point.y = last.y;
        }
    },
    finishGeometry: function () {
        var index = this.line.geometry.components.length - 2;
        this.line.geometry.removeComponent(this.line.geometry.components[index]);
        this.removePoint();
        this.finalize();
    },
    finalizeInteriorRing: function () {
        var ring = this.line.geometry;
        var modified = (ring.getArea() !== 0);
        if (modified) {
            var rings = this.polygon.geometry.components;
            for (var i = rings.length - 2; i >= 0; --i) {
                if (ring.intersects(rings[i])) {
                    modified = false;
                    break;
                }
            }
            if (modified) {
                var target;
                outer: for (var i = rings.length - 2; i > 0; --i) {
                    var points = rings[i].components;
                    for (var j = 0, jj = points.length; j < jj; ++j) {
                        if (ring.containsPoint(points[j])) {
                            modified = false;
                            break outer;
                        }
                    }
                }
            }
        }
        if (modified) {
            if (this.polygon.state !== OpenLayers.State.INSERT) {
                this.polygon.state = OpenLayers.State.UPDATE;
            }
        } else {
            this.polygon.geometry.removeComponent(ring);
        }
        this.restoreFeature();
        return false;
    },
    cancel: function () {
        if (this.drawingHole) {
            this.polygon.geometry.removeComponent(this.line.geometry);
            this.restoreFeature(true);
        }
        return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments);
    },
    restoreFeature: function (cancel) {
        this.control.layer.events.unregister("sketchcomplete", this, this.finalizeInteriorRing);
        this.control.layer.events.unregister("sketchmodified", this, this.enforceTopology);
        this.layer.removeFeatures([this.polygon], {
            silent: true
        });
        this.control.layer.addFeatures([this.polygon], {
            silent: true
        });
        this.drawingHole = false;
        if (!cancel) {
            this.control.layer.events.triggerEvent("sketchcomplete", {
                feature: this.polygon
            });
        }
    },
    destroyFeature: function (force) {
        OpenLayers.Handler.Path.prototype.destroyFeature.call(this, force);
        this.polygon = null;
    },
    drawFeature: function () {
        this.layer.drawFeature(this.polygon, this.style);
        this.layer.drawFeature(this.point, this.style);
    },
    getSketch: function () {
        return this.polygon;
    },
    getGeometry: function () {
        var geometry = this.polygon && this.polygon.geometry;
        if (geometry && this.multi) {
            geometry = new OpenLayers.Geometry.MultiPolygon([geometry]);
        }
        return geometry;
    },
    CLASS_NAME: "OpenLayers.Handler.Polygon"
});
OpenLayers.Control.EditingToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
    initialize: function (layer, options) {
        OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
        this.addControls([new OpenLayers.Control.Navigation()]);
        var controls = [new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, {
            'displayClass': 'olControlDrawFeaturePoint'
        }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, {
            'displayClass': 'olControlDrawFeaturePath'
        }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, {
            'displayClass': 'olControlDrawFeaturePolygon'
        })];
        this.addControls(controls);
    },
    draw: function () {
        var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
        if (this.defaultControl === null) {
            this.defaultControl = this.controls[0];
        }
        return div;
    },
    CLASS_NAME: "OpenLayers.Control.EditingToolbar"
});
OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, {
    bounds: null,
    resolution: null,
    ratio: 2,
    resFactor: null,
    response: null,
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.call(this);
        if (activated) {
            this.layer.events.on({
                "moveend": this.update,
                scope: this
            });
            this.layer.events.on({
                "refresh": this.update,
                scope: this
            });
            if (this.layer.visibility === true && this.layer.inRange === true) {
                this.update();
            } else {
                this.layer.events.on({
                    "visibilitychanged": this.update,
                    scope: this
                });
            }
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            this.layer.events.un({
                "moveend": this.update,
                "refresh": this.update,
                "visibilitychanged": this.update,
                scope: this
            });
        }
        return deactivated;
    },
    update: function (options) {
        var mapBounds = this.getMapBounds();
        if (mapBounds !== null && ((options && options.force) || this.invalidBounds(mapBounds))) {
            this.calculateBounds(mapBounds);
            this.resolution = this.layer.map.getResolution();
            this.triggerRead(options);
        }
    },
    getMapBounds: function () {
        if (this.layer.map === null) {
            return null;
        }
        var bounds = this.layer.map.getExtent();
        if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) {
            bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection);
        }
        return bounds;
    },
    invalidBounds: function (mapBounds) {
        if (!mapBounds) {
            mapBounds = this.getMapBounds();
        }
        var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds);
        if (!invalid && this.resFactor) {
            var ratio = this.resolution / this.layer.map.getResolution();
            invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor));
        }
        return invalid;
    },
    calculateBounds: function (mapBounds) {
        if (!mapBounds) {
            mapBounds = this.getMapBounds();
        }
        var center = mapBounds.getCenterLonLat();
        var dataWidth = mapBounds.getWidth() * this.ratio;
        var dataHeight = mapBounds.getHeight() * this.ratio;
        this.bounds = new OpenLayers.Bounds(center.lon - (dataWidth / 2), center.lat - (dataHeight / 2), center.lon + (dataWidth / 2), center.lat + (dataHeight / 2));
    },
    triggerRead: function (options) {
        if (this.response) {
            this.layer.protocol.abort(this.response);
            this.layer.events.triggerEvent("loadend");
        }
        this.layer.events.triggerEvent("loadstart");
        this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({
            filter: this.createFilter(),
            callback: this.merge,
            scope: this
        }, options));
    },
    createFilter: function () {
        var filter = new OpenLayers.Filter.Spatial({
            type: OpenLayers.Filter.Spatial.BBOX,
            value: this.bounds,
            projection: this.layer.projection
        });
        if (this.layer.filter) {
            filter = new OpenLayers.Filter.Logical({
                type: OpenLayers.Filter.Logical.AND,
                filters: [this.layer.filter, filter]
            });
        }
        return filter;
    },
    merge: function (resp) {
        this.layer.destroyFeatures();
        var features = resp.features;
        if (features && features.length > 0) {
            var remote = this.layer.projection;
            var local = this.layer.map.getProjectionObject();
            if (!local.equals(remote)) {
                var geom;
                for (var i = 0, len = features.length; i < len; ++i) {
                    geom = features[i].geometry;
                    if (geom) {
                        geom.transform(remote, local);
                    }
                }
            }
            this.layer.addFeatures(features);
        }
        this.response = null;
        this.layer.events.triggerEvent("loadend");
    },
    CLASS_NAME: "OpenLayers.Strategy.BBOX"
});
OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, {
    DEFAULT_PARAMS: {},
    isBaseLayer: true,
    lzd: null,
    zoomLevels: null,
    initialize: function (name, url, lzd, zoomLevels, params, options) {
        this.lzd = lzd;
        this.zoomLevels = zoomLevels;
        var newArguments = [];
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
        this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS);
    },
    getZoom: function () {
        var zoom = this.map.getZoom();
        var extent = this.map.getMaxExtent();
        zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2);
        return zoom;
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var zoom = this.getZoom();
        var extent = this.map.getMaxExtent();
        var deg = this.lzd / Math.pow(2, this.getZoom());
        var x = Math.floor((bounds.left - extent.left) / deg);
        var y = Math.floor((bounds.bottom - extent.bottom) / deg);
        if (this.map.getResolution() <= (this.lzd / 512) && this.getZoom() <= this.zoomLevels) {
            return this.getFullRequestString({
                L: zoom,
                X: x,
                Y: y
            });
        } else {
            return OpenLayers.Util.getImagesLocation() + "blank.gif";
        }
    },
    CLASS_NAME: "OpenLayers.Layer.WorldWind"
});
OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    yx: {
        "urn:ogc:def:crs:EPSG::4326": true
    },
    createLayer: function (capabilities, config) {
        var layer;
        var required = {
            layer: true,
            matrixSet: true
        };
        for (var prop in required) {
            if (!(prop in config)) {
                throw new Error("Missing property '" + prop + "' in layer configuration.");
            }
        }
        var contents = capabilities.contents;
        var matrixSet = contents.tileMatrixSets[config.matrixSet];
        var layers = contents.layers;
        var layerDef;
        for (var i = 0, ii = contents.layers.length; i < ii; ++i) {
            if (contents.layers[i].identifier === config.layer) {
                layerDef = contents.layers[i];
                break;
            }
        }
        if (layerDef && matrixSet) {
            var style;
            for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) {
                style = layerDef.styles[i];
                if (style.isDefault) {
                    break;
                }
            }
            layer = new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults(config, {
                url: capabilities.operationsMetadata.GetTile.dcp.http.get,
                name: layerDef.title,
                style: style.identifier,
                matrixIds: matrixSet.matrixIds
            }));
        }
        return layer;
    },
    CLASS_NAME: "OpenLayers.Format.WMTSCapabilities"
});
OpenLayers.Layer.Google.v3 = {
    DEFAULTS: {
        maxExtent: new OpenLayers.Bounds(-128 * 156543.03390625, -128 * 156543.03390625, 128 * 156543.03390625, 128 * 156543.03390625),
        sphericalMercator: true,
        maxResolution: 156543.03390625,
        units: "m",
        projection: "EPSG:900913"
    },
    animationEnabled: true,
    loadMapObject: function () {
        if (!this.type) {
            this.type = google.maps.MapTypeId.ROADMAP;
        }
        var mapObject;
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            mapObject = cache.mapObject;
            ++cache.count;
        } else {
            var container = this.map.viewPortDiv;
            var div = document.createElement("div");
            div.id = this.map.id + "_GMapContainer";
            div.style.position = "absolute";
            div.style.width = "100%";
            div.style.height = "100%";
            container.appendChild(div);
            var center = this.map.getCenter();
            mapObject = new google.maps.Map(div, {
                center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0),
                zoom: this.map.getZoom() || 0,
                mapTypeId: this.type,
                disableDefaultUI: true,
                keyboardShortcuts: false,
                draggable: false,
                disableDoubleClickZoom: true,
                scrollwheel: false,
                streetViewControl: false
            });
            cache = {
                mapObject: mapObject,
                count: 1
            };
            OpenLayers.Layer.Google.cache[this.map.id] = cache;
            this.repositionListener = google.maps.event.addListenerOnce(mapObject, "center_changed", OpenLayers.Function.bind(this.repositionMapElements, this));
        }
        this.mapObject = mapObject;
        this.setGMapVisibility(this.visibility);
    },
    repositionMapElements: function () {
        google.maps.event.trigger(this.mapObject, "resize");
        var div = this.mapObject.getDiv().firstChild;
        if (!div || div.childNodes.length < 3) {
            this.repositionTimer = window.setTimeout(OpenLayers.Function.bind(this.repositionMapElements, this), 250);
            return false;
        }
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        var container = this.map.viewPortDiv;
        while (div.lastChild.style.display == "none") {
            container.appendChild(div.lastChild);
        }
        var termsOfUse = div.lastChild;
        container.appendChild(termsOfUse);
        termsOfUse.style.zIndex = "1100";
        termsOfUse.style.bottom = "";
        termsOfUse.className = "olLayerGoogleCopyright olLayerGoogleV3";
        termsOfUse.style.display = "";
        cache.termsOfUse = termsOfUse;
        var poweredBy = div.lastChild;
        container.appendChild(poweredBy);
        poweredBy.style.zIndex = "1100";
        poweredBy.style.bottom = "";
        poweredBy.className = "olLayerGooglePoweredBy olLayerGoogleV3 gmnoprint";
        poweredBy.style.display = "";
        cache.poweredBy = poweredBy;
        this.setGMapVisibility(this.visibility);
    },
    onMapResize: function () {
        if (this.visibility) {
            google.maps.event.trigger(this.mapObject, "resize");
        } else {
            var cache = OpenLayers.Layer.Google.cache[this.map.id];
            if (!cache.resized) {
                var layer = this;
                google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function () {
                    google.maps.event.trigger(layer.mapObject, "resize");
                    layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
                    delete cache.resized;
                });
            }
            cache.resized = true;
        }
    },
    setGMapVisibility: function (visible) {
        var cache = OpenLayers.Layer.Google.cache[this.map.id];
        if (cache) {
            var type = this.type;
            var layers = this.map.layers;
            var layer;
            for (var i = layers.length - 1; i >= 0; --i) {
                layer = layers[i];
                if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) {
                    type = layer.type;
                    visible = true;
                    break;
                }
            }
            var container = this.mapObject.getDiv();
            if (visible === true) {
                this.mapObject.setMapTypeId(type);
                container.style.left = "";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.left = "";
                    cache.termsOfUse.style.display = "";
                    cache.poweredBy.style.display = "";
                }
                cache.displayed = this.id;
            } else {
                delete cache.displayed;
                container.style.left = "-9999px";
                if (cache.termsOfUse && cache.termsOfUse.style) {
                    cache.termsOfUse.style.display = "none";
                    cache.termsOfUse.style.left = "-9999px";
                    cache.poweredBy.style.display = "none";
                }
            }
        }
    },
    getMapContainer: function () {
        return this.mapObject.getDiv();
    },
    getMapObjectBoundsFromOLBounds: function (olBounds) {
        var moBounds = null;
        if (olBounds != null) {
            var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left);
            var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right);
            moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon));
        }
        return moBounds;
    },
    getMapObjectLonLatFromMapObjectPixel: function (moPixel) {
        var size = this.map.getSize();
        var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center);
        var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center);
        var res = this.map.getResolution();
        var delta_x = moPixel.x - (size.w / 2);
        var delta_y = moPixel.y - (size.h / 2);
        var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res);
        if (this.wrapDateLine) {
            lonlat = lonlat.wrapDateLine(this.maxExtent);
        }
        return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat);
    },
    getMapObjectPixelFromMapObjectLonLat: function (moLonLat) {
        var lon = this.getLongitudeFromMapObjectLonLat(moLonLat);
        var lat = this.getLatitudeFromMapObjectLonLat(moLonLat);
        var res = this.map.getResolution();
        var extent = this.map.getExtent();
        var px = new OpenLayers.Pixel((1 / res * (lon - extent.left)), (1 / res * (extent.top - lat)));
        return this.getMapObjectPixelFromXY(px.x, px.y);
    },
    setMapObjectCenter: function (center, zoom) {
        if (this.animationEnabled === false && zoom != this.mapObject.zoom) {
            var mapContainer = this.getMapContainer();
            google.maps.event.addListenerOnce(this.mapObject, "idle", function () {
                mapContainer.style.visibility = "";
            });
            mapContainer.style.visibility = "hidden";
        }
        this.mapObject.setOptions({
            center: center,
            zoom: zoom
        });
    },
    getMapObjectZoomFromMapObjectBounds: function (moBounds) {
        return this.mapObject.getBoundsZoomLevel(moBounds);
    },
    getMapObjectLonLatFromLonLat: function (lon, lat) {
        var gLatLng;
        if (this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon);
        } else {
            gLatLng = new google.maps.LatLng(lat, lon);
        }
        return gLatLng;
    },
    getMapObjectPixelFromXY: function (x, y) {
        return new google.maps.Point(x, y);
    },
    destroy: function () {
        if (this.repositionListener) {
            google.maps.event.removeListener(this.repositionListener);
        }
        if (this.repositionTimer) {
            window.clearTimeout(this.repositionTimer);
        }
        OpenLayers.Layer.Google.prototype.destroy.apply(this, arguments);
    }
};
OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(OpenLayers.Format.XML, {
    VERSION: "1.0.0",
    namespaces: {
        wps: "http://www.opengis.net/wps/1.0.0",
        ows: "http://www.opengis.net/ows/1.1",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd",
    defaultPrefix: "wps",
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var info = {};
        this.readNode(data, info);
        return info;
    },
    readers: {
        "wps": {
            "ProcessDescriptions": function (node, obj) {
                obj.processDescriptions = {};
                this.readChildNodes(node, obj.processDescriptions);
            },
            "ProcessDescription": function (node, processDescriptions) {
                var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion");
                var processDescription = {
                    processVersion: processVersion,
                    statusSupported: (node.getAttribute("statusSupported") === "true"),
                    storeSupported: (node.getAttribute("storeSupported") === "true")
                };
                this.readChildNodes(node, processDescription);
                processDescriptions[processDescription.identifier] = processDescription;
            },
            "DataInputs": function (node, processDescription) {
                processDescription.dataInputs = [];
                this.readChildNodes(node, processDescription.dataInputs);
            },
            "ProcessOutputs": function (node, processDescription) {
                processDescription.processOutputs = [];
                this.readChildNodes(node, processDescription.processOutputs);
            },
            "Output": function (node, processOutputs) {
                var output = {};
                this.readChildNodes(node, output);
                processOutputs.push(output);
            },
            "ComplexOutput": function (node, output) {
                output.complexOutput = {};
                this.readChildNodes(node, output.complexOutput);
            },
            "Input": function (node, dataInputs) {
                var input = {
                    maxOccurs: parseInt(node.getAttribute("maxOccurs")),
                    minOccurs: parseInt(node.getAttribute("minOccurs"))
                };
                this.readChildNodes(node, input);
                dataInputs.push(input);
            },
            "BoundingBoxData": function (node, input) {
                input.boundingBoxData = {};
                this.readChildNodes(node, input.boundingBoxData);
            },
            "CRS": function (node, obj) {
                if (!obj.CRSs) {
                    obj.CRSs = {};
                }
                obj.CRSs[this.getChildValue(node)] = true;
            },
            "LiteralData": function (node, input) {
                input.literalData = {};
                this.readChildNodes(node, input.literalData);
            },
            "ComplexData": function (node, input) {
                input.complexData = {};
                this.readChildNodes(node, input.complexData);
            },
            "Default": function (node, complexData) {
                complexData["default"] = {};
                this.readChildNodes(node, complexData["default"]);
            },
            "Supported": function (node, complexData) {
                complexData["supported"] = {};
                this.readChildNodes(node, complexData["supported"]);
            },
            "Format": function (node, obj) {
                var format = {};
                this.readChildNodes(node, format);
                if (!obj.formats) {
                    obj.formats = {};
                }
                obj.formats[format.mimeType] = true;
            },
            "MimeType": function (node, format) {
                format.mimeType = this.getChildValue(node);
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
    },
    CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess"
});
OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, {
    initialize: function (options) {
        OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]);
        this.addControls([new OpenLayers.Control.Navigation(), new OpenLayers.Control.ZoomBox()]);
    },
    draw: function () {
        var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
        if (this.defaultControl === null) {
            this.defaultControl = this.controls[0];
        }
        return div;
    },
    CLASS_NAME: "OpenLayers.Control.NavToolbar"
});
OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        csw: "http://www.opengis.net/cat/csw/2.0.2",
        dc: "http://purl.org/dc/elements/1.1/",
        dct: "http://purl.org/dc/terms/",
        geonet: "http://www.fao.org/geonetwork",
        ogc: "http://www.opengis.net/ogc",
        ows: "http://www.opengis.net/ows",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    defaultPrefix: "csw",
    version: "2.0.2",
    schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",
    requestId: null,
    resultType: null,
    outputFormat: null,
    outputSchema: null,
    startPosition: null,
    maxRecords: null,
    DistributedSearch: null,
    ResponseHandler: null,
    Query: null,
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var obj = {};
        this.readNode(data, obj);
        return obj;
    },
    readers: {
        "csw": {
            "GetRecordsResponse": function (node, obj) {
                obj.records = [];
                this.readChildNodes(node, obj);
                var version = this.getAttributeNS(node, "", 'version');
                if (version != "") {
                    obj.version = version;
                }
            },
            "RequestId": function (node, obj) {
                obj.RequestId = this.getChildValue(node);
            },
            "SearchStatus": function (node, obj) {
                obj.SearchStatus = {};
                var timestamp = this.getAttributeNS(node, "", 'timestamp');
                if (timestamp != "") {
                    obj.SearchStatus.timestamp = timestamp;
                }
            },
            "SearchResults": function (node, obj) {
                this.readChildNodes(node, obj);
                var attrs = node.attributes;
                var SearchResults = {};
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    if ((attrs[i].name == "numberOfRecordsMatched") || (attrs[i].name == "numberOfRecordsReturned") || (attrs[i].name == "nextRecord")) {
                        SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue);
                    } else {
                        SearchResults[attrs[i].name] = attrs[i].nodeValue;
                    }
                }
                obj.SearchResults = SearchResults;
            },
            "SummaryRecord": function (node, obj) {
                var record = {
                    type: "SummaryRecord"
                };
                this.readChildNodes(node, record);
                obj.records.push(record);
            },
            "BriefRecord": function (node, obj) {
                var record = {
                    type: "BriefRecord"
                };
                this.readChildNodes(node, record);
                obj.records.push(record);
            },
            "DCMIRecord": function (node, obj) {
                var record = {
                    type: "DCMIRecord"
                };
                this.readChildNodes(node, record);
                obj.records.push(record);
            },
            "Record": function (node, obj) {
                var record = {
                    type: "Record"
                };
                this.readChildNodes(node, record);
                obj.records.push(record);
            },
            "*": function (node, obj) {
                var name = node.localName || node.nodeName.split(":").pop();
                obj[name] = this.getChildValue(node);
            }
        },
        "geonet": {
            "info": function (node, obj) {
                var gninfo = {};
                this.readChildNodes(node, gninfo);
                obj.gninfo = gninfo;
            }
        },
        "dc": {
            "*": function (node, obj) {
                var name = node.localName || node.nodeName.split(":").pop();
                if (!(OpenLayers.Util.isArray(obj[name]))) {
                    obj[name] = new Array();
                }
                var dc_element = {};
                var attrs = node.attributes;
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    dc_element[attrs[i].name] = attrs[i].nodeValue;
                }
                dc_element.value = this.getChildValue(node);
                obj[name].push(dc_element);
            }
        },
        "dct": {
            "*": function (node, obj) {
                var name = node.localName || node.nodeName.split(":").pop();
                if (!(OpenLayers.Util.isArray(obj[name]))) {
                    obj[name] = new Array();
                }
                obj[name].push(this.getChildValue(node));
            }
        },
        "ows": OpenLayers.Util.applyDefaults({
            "BoundingBox": function (node, obj) {
                if (obj.bounds) {
                    obj.BoundingBox = [{
                        crs: obj.projection,
                        value: [obj.bounds.left, obj.bounds.bottom, obj.bounds.right, obj.bounds.top]
                    }];
                    delete obj.projection;
                    delete obj.bounds;
                }
                OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply(this, arguments);
            }
        }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"])
    },
    write: function (options) {
        var node = this.writeNode("csw:GetRecords", options);
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    writers: {
        "csw": {
            "GetRecords": function (options) {
                if (!options) {
                    options = {};
                }
                var node = this.createElementNSPlus("csw:GetRecords", {
                    attributes: {
                        service: "CSW",
                        version: this.version,
                        requestId: options.requestId || this.requestId,
                        resultType: options.resultType || this.resultType,
                        outputFormat: options.outputFormat || this.outputFormat,
                        outputSchema: options.outputSchema || this.outputSchema,
                        startPosition: options.startPosition || this.startPosition,
                        maxRecords: options.maxRecords || this.maxRecords
                    }
                });
                if (options.DistributedSearch || this.DistributedSearch) {
                    this.writeNode("csw:DistributedSearch", options.DistributedSearch || this.DistributedSearch, node);
                }
                var ResponseHandler = options.ResponseHandler || this.ResponseHandler;
                if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) {
                    for (var i = 0, len = ResponseHandler.length; i < len; i++) {
                        this.writeNode("csw:ResponseHandler", ResponseHandler[i], node);
                    }
                }
                this.writeNode("Query", options.Query || this.Query, node);
                return node;
            },
            "DistributedSearch": function (options) {
                var node = this.createElementNSPlus("csw:DistributedSearch", {
                    attributes: {
                        hopCount: options.hopCount
                    }
                });
                return node;
            },
            "ResponseHandler": function (options) {
                var node = this.createElementNSPlus("csw:ResponseHandler", {
                    value: options.value
                });
                return node;
            },
            "Query": function (options) {
                if (!options) {
                    options = {};
                }
                var node = this.createElementNSPlus("csw:Query", {
                    attributes: {
                        typeNames: options.typeNames || "csw:Record"
                    }
                });
                var ElementName = options.ElementName;
                if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) {
                    for (var i = 0, len = ElementName.length; i < len; i++) {
                        this.writeNode("csw:ElementName", ElementName[i], node);
                    }
                } else {
                    this.writeNode("csw:ElementSetName", options.ElementSetName || {
                        value: 'summary'
                    }, node);
                }
                if (options.Constraint) {
                    this.writeNode("csw:Constraint", options.Constraint, node);
                }
                if (options.SortBy) {
                    this.writeNode("ogc:SortBy", options.SortBy, node);
                }
                return node;
            },
            "ElementName": function (options) {
                var node = this.createElementNSPlus("csw:ElementName", {
                    value: options.value
                });
                return node;
            },
            "ElementSetName": function (options) {
                var node = this.createElementNSPlus("csw:ElementSetName", {
                    attributes: {
                        typeNames: options.typeNames
                    },
                    value: options.value
                });
                return node;
            },
            "Constraint": function (options) {
                var node = this.createElementNSPlus("csw:Constraint", {
                    attributes: {
                        version: options.version
                    }
                });
                if (options.Filter) {
                    var format = new OpenLayers.Format.Filter({
                        version: options.version
                    });
                    node.appendChild(format.write(options.Filter));
                } else if (options.CqlText) {
                    var child = this.createElementNSPlus("CqlText", {
                        value: options.CqlText.value
                    });
                    node.appendChild(child);
                }
                return node;
            }
        },
        "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"]
    },
    CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2"
});
OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, {
    bounds: null,
    div: null,
    initialize: function (bounds, borderColor, borderWidth) {
        this.bounds = bounds;
        this.div = OpenLayers.Util.createDiv();
        this.div.style.overflow = 'hidden';
        this.events = new OpenLayers.Events(this, this.div, null);
        this.setBorder(borderColor, borderWidth);
    },
    destroy: function () {
        this.bounds = null;
        this.div = null;
        OpenLayers.Marker.prototype.destroy.apply(this, arguments);
    },
    setBorder: function (color, width) {
        if (!color) {
            color = "red";
        }
        if (!width) {
            width = 2;
        }
        this.div.style.border = width + "px solid " + color;
    },
    draw: function (px, sz) {
        OpenLayers.Util.modifyDOMElement(this.div, null, px, sz);
        return this.div;
    },
    onScreen: function () {
        var onScreen = false;
        if (this.map) {
            var screenBounds = this.map.getExtent();
            onScreen = screenBounds.containsBounds(this.bounds, true, true);
        }
        return onScreen;
    },
    display: function (display) {
        this.div.style.display = (display) ? "" : "none";
    },
    CLASS_NAME: "OpenLayers.Marker.Box"
});
OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, {
    defaultStyle: null,
    extractStyles: true,
    initialize: function (options) {
        options = options || {};
        if (options.extractStyles !== false) {
            options.defaultStyle = {
                'externalGraphic': OpenLayers.Util.getImagesLocation() + "marker.png",
                'graphicWidth': 21,
                'graphicHeight': 25,
                'graphicXOffset': -10.5,
                'graphicYOffset': -12.5
            };
        }
        OpenLayers.Format.prototype.initialize.apply(this, [options]);
    },
    read: function (text) {
        var lines = text.split('\n');
        var columns;
        var features = [];
        for (var lcv = 0; lcv < (lines.length - 1); lcv++) {
            var currLine = lines[lcv].replace(/^\s*/, '').replace(/\s*$/, '');
            if (currLine.charAt(0) != '#') {
                if (!columns) {
                    columns = currLine.split('\t');
                } else {
                    var vals = currLine.split('\t');
                    var geometry = new OpenLayers.Geometry.Point(0, 0);
                    var attributes = {};
                    var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null;
                    var icon, iconSize, iconOffset, overflow;
                    var set = false;
                    for (var valIndex = 0; valIndex < vals.length; valIndex++) {
                        if (vals[valIndex]) {
                            if (columns[valIndex] == 'point') {
                                var coords = vals[valIndex].split(',');
                                geometry.y = parseFloat(coords[0]);
                                geometry.x = parseFloat(coords[1]);
                                set = true;
                            } else if (columns[valIndex] == 'lat') {
                                geometry.y = parseFloat(vals[valIndex]);
                                set = true;
                            } else if (columns[valIndex] == 'lon') {
                                geometry.x = parseFloat(vals[valIndex]);
                                set = true;
                            } else if (columns[valIndex] == 'title') attributes['title'] = vals[valIndex];
                            else if (columns[valIndex] == 'image' || columns[valIndex] == 'icon' && style) {
                                style['externalGraphic'] = vals[valIndex];
                            } else if (columns[valIndex] == 'iconSize' && style) {
                                var size = vals[valIndex].split(',');
                                style['graphicWidth'] = parseFloat(size[0]);
                                style['graphicHeight'] = parseFloat(size[1]);
                            } else if (columns[valIndex] == 'iconOffset' && style) {
                                var offset = vals[valIndex].split(',');
                                style['graphicXOffset'] = parseFloat(offset[0]);
                                style['graphicYOffset'] = parseFloat(offset[1]);
                            } else if (columns[valIndex] == 'description') {
                                attributes['description'] = vals[valIndex];
                            } else if (columns[valIndex] == 'overflow') {
                                attributes['overflow'] = vals[valIndex];
                            } else {
                                attributes[columns[valIndex]] = vals[valIndex];
                            }
                        }
                    }
                    if (set) {
                        if (this.internalProjection && this.externalProjection) {
                            geometry.transform(this.externalProjection, this.internalProjection);
                        }
                        var feature = new OpenLayers.Feature.Vector(geometry, attributes, style);
                        features.push(feature);
                    }
                }
            }
        }
        return features;
    },
    CLASS_NAME: "OpenLayers.Format.Text"
});
OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, {
    location: null,
    features: null,
    formatOptions: null,
    selectedFeature: null,
    initialize: function (name, options) {
        OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
        this.features = new Array();
    },
    destroy: function () {
        OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
        this.clearFeatures();
        this.features = null;
    },
    loadText: function () {
        if (!this.loaded) {
            if (this.location != null) {
                var onFail = function (e) {
                        this.events.triggerEvent("loadend");
                    };
                this.events.triggerEvent("loadstart");
                OpenLayers.Request.GET({
                    url: this.location,
                    success: this.parseData,
                    failure: onFail,
                    scope: this
                });
                this.loaded = true;
            }
        }
    },
    moveTo: function (bounds, zoomChanged, minor) {
        OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
        if (this.visibility && !this.loaded) {
            this.loadText();
        }
    },
    parseData: function (ajaxRequest) {
        var text = ajaxRequest.responseText;
        var options = {};
        OpenLayers.Util.extend(options, this.formatOptions);
        if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
            options.externalProjection = this.projection;
            options.internalProjection = this.map.getProjectionObject();
        }
        var parser = new OpenLayers.Format.Text(options);
        var features = parser.read(text);
        for (var i = 0, len = features.length; i < len; i++) {
            var data = {};
            var feature = features[i];
            var location;
            var iconSize, iconOffset;
            location = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y);
            if (feature.style.graphicWidth && feature.style.graphicHeight) {
                iconSize = new OpenLayers.Size(feature.style.graphicWidth, feature.style.graphicHeight);
            }
            if (feature.style.graphicXOffset !== undefined && feature.style.graphicYOffset !== undefined) {
                iconOffset = new OpenLayers.Pixel(feature.style.graphicXOffset, feature.style.graphicYOffset);
            }
            if (feature.style.externalGraphic != null) {
                data.icon = new OpenLayers.Icon(feature.style.externalGraphic, iconSize, iconOffset);
            } else {
                data.icon = OpenLayers.Marker.defaultIcon();
                if (iconSize != null) {
                    data.icon.setSize(iconSize);
                }
            }
            if ((feature.attributes.title != null) && (feature.attributes.description != null)) {
                data['popupContentHTML'] = '<h2>' + feature.attributes.title + '</h2>' + '<p>' + feature.attributes.description + '</p>';
            }
            data['overflow'] = feature.attributes.overflow || "auto";
            var markerFeature = new OpenLayers.Feature(this, location, data);
            this.features.push(markerFeature);
            var marker = markerFeature.createMarker();
            if ((feature.attributes.title != null) && (feature.attributes.description != null)) {
                marker.events.register('click', markerFeature, this.markerClick);
            }
            this.addMarker(marker);
        }
        this.events.triggerEvent("loadend");
    },
    markerClick: function (evt) {
        var sameMarkerClicked = (this == this.layer.selectedFeature);
        this.layer.selectedFeature = (!sameMarkerClicked) ? this : null;
        for (var i = 0, len = this.layer.map.popups.length; i < len; i++) {
            this.layer.map.removePopup(this.layer.map.popups[i]);
        }
        if (!sameMarkerClicked) {
            this.layer.map.addPopup(this.createPopup());
        }
        OpenLayers.Event.stop(evt);
    },
    clearFeatures: function () {
        if (this.features != null) {
            while (this.features.length > 0) {
                var feature = this.features[0];
                OpenLayers.Util.removeItem(this.features, feature);
                feature.destroy();
            }
        }
    },
    CLASS_NAME: "OpenLayers.Layer.Text"
});
OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, {
    sides: 4,
    radius: null,
    snapAngle: null,
    snapToggle: 'shiftKey',
    layerOptions: null,
    persist: false,
    irregular: false,
    angle: null,
    fixedRadius: false,
    feature: null,
    layer: null,
    origin: null,
    initialize: function (control, callbacks, options) {
        if (!(options && options.layerOptions && options.layerOptions.styleMap)) {
            this.style = OpenLayers.Util.extend(OpenLayers.Feature.Vector.style['default'], {});
        }
        OpenLayers.Handler.Drag.prototype.initialize.apply(this, [control, callbacks, options]);
        this.options = (options) ? options : {};
    },
    setOptions: function (newOptions) {
        OpenLayers.Util.extend(this.options, newOptions);
        OpenLayers.Util.extend(this, newOptions);
    },
    activate: function () {
        var activated = false;
        if (OpenLayers.Handler.Drag.prototype.activate.apply(this, arguments)) {
            var options = OpenLayers.Util.extend({
                displayInLayerSwitcher: false,
                calculateInRange: OpenLayers.Function.True
            }, this.layerOptions);
            this.layer = new OpenLayers.Layer.Vector(this.CLASS_NAME, options);
            this.map.addLayer(this.layer);
            activated = true;
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.Drag.prototype.deactivate.apply(this, arguments)) {
            if (this.dragging) {
                this.cancel();
            }
            if (this.layer.map != null) {
                this.layer.destroy(false);
                if (this.feature) {
                    this.feature.destroy();
                }
            }
            this.layer = null;
            this.feature = null;
            deactivated = true;
        }
        return deactivated;
    },
    down: function (evt) {
        this.fixedRadius = !! (this.radius);
        var maploc = this.map.getLonLatFromPixel(evt.xy);
        this.origin = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
        if (!this.fixedRadius || this.irregular) {
            this.radius = this.map.getResolution();
        }
        if (this.persist) {
            this.clear();
        }
        this.feature = new OpenLayers.Feature.Vector();
        this.createGeometry();
        this.callback("create", [this.origin, this.feature]);
        this.layer.addFeatures([this.feature], {
            silent: true
        });
        this.layer.drawFeature(this.feature, this.style);
    },
    move: function (evt) {
        var maploc = this.map.getLonLatFromPixel(evt.xy);
        var point = new OpenLayers.Geometry.Point(maploc.lon, maploc.lat);
        if (this.irregular) {
            var ry = Math.sqrt(2) * Math.abs(point.y - this.origin.y) / 2;
            this.radius = Math.max(this.map.getResolution() / 2, ry);
        } else if (this.fixedRadius) {
            this.origin = point;
        } else {
            this.calculateAngle(point, evt);
            this.radius = Math.max(this.map.getResolution() / 2, point.distanceTo(this.origin));
        }
        this.modifyGeometry();
        if (this.irregular) {
            var dx = point.x - this.origin.x;
            var dy = point.y - this.origin.y;
            var ratio;
            if (dy == 0) {
                ratio = dx / (this.radius * Math.sqrt(2));
            } else {
                ratio = dx / dy;
            }
            this.feature.geometry.resize(1, this.origin, ratio);
            this.feature.geometry.move(dx / 2, dy / 2);
        }
        this.layer.drawFeature(this.feature, this.style);
    },
    up: function (evt) {
        this.finalize();
        if (this.start == this.last) {
            this.callback("done", [evt.xy]);
        }
    },
    out: function (evt) {
        this.finalize();
    },
    createGeometry: function () {
        this.angle = Math.PI * ((1 / this.sides) - (1 / 2));
        if (this.snapAngle) {
            this.angle += this.snapAngle * (Math.PI / 180);
        }
        this.feature.geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(this.origin, this.radius, this.sides, this.snapAngle);
    },
    modifyGeometry: function () {
        var angle, point;
        var ring = this.feature.geometry.components[0];
        if (ring.components.length != (this.sides + 1)) {
            this.createGeometry();
            ring = this.feature.geometry.components[0];
        }
        for (var i = 0; i < this.sides; ++i) {
            point = ring.components[i];
            angle = this.angle + (i * 2 * Math.PI / this.sides);
            point.x = this.origin.x + (this.radius * Math.cos(angle));
            point.y = this.origin.y + (this.radius * Math.sin(angle));
            point.clearBounds();
        }
    },
    calculateAngle: function (point, evt) {
        var alpha = Math.atan2(point.y - this.origin.y, point.x - this.origin.x);
        if (this.snapAngle && (this.snapToggle && !evt[this.snapToggle])) {
            var snapAngleRad = (Math.PI / 180) * this.snapAngle;
            this.angle = Math.round(alpha / snapAngleRad) * snapAngleRad;
        } else {
            this.angle = alpha;
        }
    },
    cancel: function () {
        this.callback("cancel", null);
        this.finalize();
    },
    finalize: function () {
        this.origin = null;
        this.radius = this.options.radius;
    },
    clear: function () {
        if (this.layer) {
            this.layer.renderer.clear();
            this.layer.destroyFeatures();
        }
    },
    callback: function (name, args) {
        if (this.callbacks[name]) {
            this.callbacks[name].apply(this.control, [this.feature.geometry.clone()]);
        }
        if (!this.persist && (name == "done" || name == "cancel")) {
            this.clear();
        }
    },
    CLASS_NAME: "OpenLayers.Handler.RegularPolygon"
});
OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["selected"],
    clearOnDeactivate: false,
    layers: null,
    callbacks: null,
    selectionSymbolizer: {
        'Polygon': {
            fillColor: '#FF0000',
            stroke: false
        },
        'Line': {
            strokeColor: '#FF0000',
            strokeWidth: 2
        },
        'Point': {
            graphicName: 'square',
            fillColor: '#FF0000',
            pointRadius: 5
        }
    },
    layerOptions: null,
    handlerOptions: null,
    sketchStyle: null,
    wfsCache: {},
    layerCache: {},
    initialize: function (handler, options) {
        this.EVENT_TYPES = OpenLayers.Control.SLDSelect.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.callbacks = OpenLayers.Util.extend({
            done: this.select,
            click: this.select
        }, this.callbacks);
        this.handlerOptions = this.handlerOptions || {};
        this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, {
            displayInLayerSwitcher: false,
            tileOptions: {
                maxGetUrlLength: 2048
            }
        });
        if (this.sketchStyle) {
            this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, {
                styleMap: new OpenLayers.StyleMap({
                    "default": this.sketchStyle
                })
            });
        }
        this.handler = new handler(this, this.callbacks, this.handlerOptions);
    },
    destroy: function () {
        for (var key in this.layerCache) {
            delete this.layerCache[key];
        }
        for (var key in this.wfsCache) {
            delete this.wfsCache[key];
        }
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    coupleLayerVisiblity: function (evt) {
        this.setVisibility(evt.object.getVisibility());
    },
    createSelectionLayer: function (source) {
        var selectionLayer;
        if (!this.layerCache[source.id]) {
            selectionLayer = new OpenLayers.Layer.WMS(source.name, source.url, source.params, OpenLayers.Util.applyDefaults(this.layerOptions, source.getOptions()));
            this.layerCache[source.id] = selectionLayer;
            if (this.layerOptions.displayInLayerSwitcher === false) {
                source.events.on({
                    "visibilitychanged": this.coupleLayerVisiblity,
                    scope: selectionLayer
                });
            }
            this.map.addLayer(selectionLayer);
        } else {
            selectionLayer = this.layerCache[source.id];
        }
        return selectionLayer;
    },
    createSLD: function (layer, filters, geometryAttributes) {
        var sld = {
            version: "1.0.0",
            namedLayers: {}
        };
        var layerNames = [layer.params.LAYERS].join(",").split(",");
        for (var i = 0, len = layerNames.length; i < len; i++) {
            var name = layerNames[i];
            sld.namedLayers[name] = {
                name: name,
                userStyles: []
            };
            var symbolizer = this.selectionSymbolizer;
            var geometryAttribute = geometryAttributes[i];
            if (geometryAttribute.type.indexOf('Polygon') >= 0) {
                symbolizer = {
                    Polygon: this.selectionSymbolizer['Polygon']
                };
            } else if (geometryAttribute.type.indexOf('LineString') >= 0) {
                symbolizer = {
                    Line: this.selectionSymbolizer['Line']
                };
            } else if (geometryAttribute.type.indexOf('Point') >= 0) {
                symbolizer = {
                    Point: this.selectionSymbolizer['Point']
                };
            }
            var filter = filters[i];
            sld.namedLayers[name].userStyles.push({
                name: 'default',
                rules: [new OpenLayers.Rule({
                    symbolizer: symbolizer,
                    filter: filter,
                    maxScaleDenominator: layer.options.minScale
                })]
            });
        }
        return new OpenLayers.Format.SLD({
            srsName: this.map.getProjection()
        }).write(sld);
    },
    parseDescribeLayer: function (request) {
        var format = new OpenLayers.Format.WMSDescribeLayer();
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        var describeLayer = format.read(doc);
        var typeNames = [];
        var url = null;
        for (var i = 0, len = describeLayer.length; i < len; i++) {
            if (describeLayer[i].owsType == "WFS") {
                typeNames.push(describeLayer[i].typeName);
                url = describeLayer[i].owsURL;
            }
        }
        var options = {
            url: url,
            params: {
                SERVICE: "WFS",
                TYPENAME: typeNames.toString(),
                REQUEST: "DescribeFeatureType",
                VERSION: "1.0.0"
            },
            callback: function (request) {
                var format = new OpenLayers.Format.WFSDescribeFeatureType();
                var doc = request.responseXML;
                if (!doc || !doc.documentElement) {
                    doc = request.responseText;
                }
                var describeFeatureType = format.read(doc);
                this.control.wfsCache[this.layer.id] = describeFeatureType;
                this.control._queue && this.control.applySelection();
            },
            scope: this
        };
        OpenLayers.Request.GET(options);
    },
    getGeometryAttributes: function (layer) {
        var result = [];
        var cache = this.wfsCache[layer.id];
        for (var i = 0, len = cache.featureTypes.length; i < len; i++) {
            var typeName = cache.featureTypes[i];
            var properties = typeName.properties;
            for (var j = 0, lenj = properties.length; j < lenj; j++) {
                var property = properties[j];
                var type = property.type;
                if ((type.indexOf('LineString') >= 0) || (type.indexOf('GeometryAssociationType') >= 0) || (type.indexOf('GeometryPropertyType') >= 0) || (type.indexOf('Point') >= 0) || (type.indexOf('Polygon') >= 0)) {
                    result.push(property);
                }
            }
        }
        return result;
    },
    activate: function () {
        var activated = OpenLayers.Control.prototype.activate.call(this);
        if (activated) {
            for (var i = 0, len = this.layers.length; i < len; i++) {
                var layer = this.layers[i];
                if (layer && !this.wfsCache[layer.id]) {
                    var options = {
                        url: layer.url,
                        params: {
                            SERVICE: "WMS",
                            VERSION: layer.params.VERSION,
                            LAYERS: layer.params.LAYERS,
                            REQUEST: "DescribeLayer"
                        },
                        callback: this.parseDescribeLayer,
                        scope: {
                            layer: layer,
                            control: this
                        }
                    };
                    OpenLayers.Request.GET(options);
                }
            }
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
        if (deactivated) {
            for (var i = 0, len = this.layers.length; i < len; i++) {
                var layer = this.layers[i];
                if (layer && this.clearOnDeactivate === true) {
                    var layerCache = this.layerCache;
                    var selectionLayer = layerCache[layer.id];
                    if (selectionLayer) {
                        layer.events.un({
                            "visibilitychanged": this.coupleLayerVisiblity,
                            scope: selectionLayer
                        });
                        selectionLayer.destroy();
                        delete layerCache[layer.id];
                    }
                }
            }
        }
        return deactivated;
    },
    setLayers: function (layers) {
        if (this.active) {
            this.deactivate();
            this.layers = layers;
            this.activate();
        } else {
            this.layers = layers;
        }
    },
    createFilter: function (geometryAttribute, geometry) {
        var filter = null;
        if (this.handler instanceof OpenLayers.Handler.RegularPolygon) {
            if (this.handler.irregular === true) {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.BBOX,
                    property: geometryAttribute.name,
                    value: geometry.getBounds()
                });
            } else {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.INTERSECTS,
                    property: geometryAttribute.name,
                    value: geometry
                });
            }
        } else if (this.handler instanceof OpenLayers.Handler.Polygon) {
            filter = new OpenLayers.Filter.Spatial({
                type: OpenLayers.Filter.Spatial.INTERSECTS,
                property: geometryAttribute.name,
                value: geometry
            });
        } else if (this.handler instanceof OpenLayers.Handler.Path) {
            if (geometryAttribute.type.indexOf('Point') >= 0) {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.DWITHIN,
                    property: geometryAttribute.name,
                    distance: this.map.getExtent().getWidth() * 0.01,
                    distanceUnits: this.map.getUnits(),
                    value: geometry
                });
            } else {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.INTERSECTS,
                    property: geometryAttribute.name,
                    value: geometry
                });
            }
        } else if (this.handler instanceof OpenLayers.Handler.Click) {
            if (geometryAttribute.type.indexOf('Polygon') >= 0) {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.INTERSECTS,
                    property: geometryAttribute.name,
                    value: geometry
                });
            } else {
                filter = new OpenLayers.Filter.Spatial({
                    type: OpenLayers.Filter.Spatial.DWITHIN,
                    property: geometryAttribute.name,
                    distance: this.map.getExtent().getWidth() * 0.01,
                    distanceUnits: this.map.getUnits(),
                    value: geometry
                });
            }
        }
        return filter;
    },
    select: function (geometry) {
        this._queue = function () {
            for (var i = 0, len = this.layers.length; i < len; i++) {
                var layer = this.layers[i];
                var geometryAttributes = this.getGeometryAttributes(layer);
                var filters = [];
                for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) {
                    var geometryAttribute = geometryAttributes[j];
                    if (geometryAttribute !== null) {
                        if (!(geometry instanceof OpenLayers.Geometry)) {
                            var point = this.map.getLonLatFromPixel(geometry.xy);
                            geometry = new OpenLayers.Geometry.Point(point.lon, point.lat);
                        }
                        var filter = this.createFilter(geometryAttribute, geometry);
                        if (filter !== null) {
                            filters.push(filter);
                        }
                    }
                }
                var selectionLayer = this.createSelectionLayer(layer);
                var sld = this.createSLD(layer, filters, geometryAttributes);
                this.events.triggerEvent("selected", {
                    layer: layer,
                    filters: filters
                });
                selectionLayer.mergeNewParams({
                    SLD_BODY: sld
                });
                delete this._queue;
            }
        };
        this.applySelection();
    },
    applySelection: function () {
        var canApply = true;
        for (var i = 0, len = this.layers.length; i < len; i++) {
            if (!this.wfsCache[this.layers[i].id]) {
                canApply = false;
                break;
            }
        }
        canApply && this._queue.call(this);
    },
    CLASS_NAME: "OpenLayers.Control.SLDSelect"
});
OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, {
    element: null,
    geodesic: false,
    initialize: function (element, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.element = OpenLayers.Util.getElement(element);
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!this.element) {
            this.element = document.createElement("div");
            this.div.appendChild(this.element);
        }
        this.map.events.register('moveend', this, this.updateScale);
        this.updateScale();
        return this.div;
    },
    updateScale: function () {
        var scale;
        if (this.geodesic === true) {
            var units = this.map.getUnits();
            if (!units) {
                return;
            }
            var inches = OpenLayers.INCHES_PER_UNIT;
            scale = (this.map.getGeodesicPixelSize().w || 0.000001) * inches["km"] * OpenLayers.DOTS_PER_INCH;
        } else {
            scale = this.map.getScale();
        }
        if (!scale) {
            return;
        }
        if (scale >= 9500 && scale <= 950000) {
            scale = Math.round(scale / 1000) + "K";
        } else if (scale >= 950000) {
            scale = Math.round(scale / 1000000) + "M";
        } else {
            scale = Math.round(scale);
        }
        this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", {
            'scaleDenom': scale
        });
    },
    CLASS_NAME: "OpenLayers.Control.Scale"
});
OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_BUTTON,
    trigger: function () {},
    CLASS_NAME: "OpenLayers.Control.Button"
});
OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    useHttpTile: false,
    singleTile: false,
    useOverlay: false,
    useAsyncOverlay: true,
    TILE_PARAMS: {
        operation: 'GETTILEIMAGE',
        version: '1.2.0'
    },
    SINGLE_TILE_PARAMS: {
        operation: 'GETMAPIMAGE',
        format: 'PNG',
        locale: 'en',
        clip: '1',
        version: '1.0.0'
    },
    OVERLAY_PARAMS: {
        operation: 'GETDYNAMICMAPOVERLAYIMAGE',
        format: 'PNG',
        locale: 'en',
        clip: '1',
        version: '2.0.0'
    },
    FOLDER_PARAMS: {
        tileColumnsPerFolder: 30,
        tileRowsPerFolder: 30,
        format: 'png',
        querystring: null
    },
    defaultSize: new OpenLayers.Size(300, 300),
    tileOriginCorner: "tl",
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments);
        if (options == null || options.isBaseLayer == null) {
            this.isBaseLayer = ((this.transparent != "true") && (this.transparent != true));
        }
        if (options && options.useOverlay != null) {
            this.useOverlay = options.useOverlay;
        }
        if (this.singleTile) {
            if (this.useOverlay) {
                OpenLayers.Util.applyDefaults(this.params, this.OVERLAY_PARAMS);
                if (!this.useAsyncOverlay) {
                    this.params.version = "1.0.0";
                }
            } else {
                OpenLayers.Util.applyDefaults(this.params, this.SINGLE_TILE_PARAMS);
            }
        } else {
            if (this.useHttpTile) {
                OpenLayers.Util.applyDefaults(this.params, this.FOLDER_PARAMS);
            } else {
                OpenLayers.Util.applyDefaults(this.params, this.TILE_PARAMS);
            }
            this.setTileSize(this.defaultSize);
        }
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.MapGuide(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        var url;
        var center = bounds.getCenterLonLat();
        var mapSize = this.map.getSize();
        if (this.singleTile) {
            var params = {
                setdisplaydpi: OpenLayers.DOTS_PER_INCH,
                setdisplayheight: mapSize.h * this.ratio,
                setdisplaywidth: mapSize.w * this.ratio,
                setviewcenterx: center.lon,
                setviewcentery: center.lat,
                setviewscale: this.map.getScale()
            };
            if (this.useOverlay && !this.useAsyncOverlay) {
                var getVisParams = {};
                getVisParams = OpenLayers.Util.extend(getVisParams, params);
                getVisParams.operation = "GETVISIBLEMAPEXTENT";
                getVisParams.version = "1.0.0";
                getVisParams.session = this.params.session;
                getVisParams.mapName = this.params.mapName;
                getVisParams.format = 'text/xml';
                url = this.getFullRequestString(getVisParams);
                OpenLayers.Request.GET({
                    url: url,
                    async: false
                });
            }
            url = this.getFullRequestString(params);
        } else {
            var currentRes = this.map.getResolution();
            var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes);
            colidx = Math.round(colidx / this.tileSize.w);
            var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes);
            rowidx = Math.round(rowidx / this.tileSize.h);
            if (this.useHttpTile) {
                url = this.getImageFilePath({
                    tilecol: colidx,
                    tilerow: rowidx,
                    scaleindex: this.resolutions.length - this.map.zoom - 1
                });
            } else {
                url = this.getFullRequestString({
                    tilecol: colidx,
                    tilerow: rowidx,
                    scaleindex: this.resolutions.length - this.map.zoom - 1
                });
            }
        }
        return url;
    },
    getFullRequestString: function (newParams, altUrl) {
        var url = (altUrl == null) ? this.url : altUrl;
        if (typeof url == "object") {
            url = url[Math.floor(Math.random() * url.length)];
        }
        var requestString = url;
        var allParams = OpenLayers.Util.extend({}, this.params);
        allParams = OpenLayers.Util.extend(allParams, newParams);
        var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
        for (var key in allParams) {
            if (key.toUpperCase() in urlParams) {
                delete allParams[key];
            }
        }
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        paramsString = paramsString.replace(/,/g, "+");
        if (paramsString != "") {
            var lastServerChar = url.charAt(url.length - 1);
            if ((lastServerChar == "&") || (lastServerChar == "?")) {
                requestString += paramsString;
            } else {
                if (url.indexOf('?') == -1) {
                    requestString += '?' + paramsString;
                } else {
                    requestString += '&' + paramsString;
                }
            }
        }
        return requestString;
    },
    getImageFilePath: function (newParams, altUrl) {
        var url = (altUrl == null) ? this.url : altUrl;
        if (typeof url == "object") {
            url = url[Math.floor(Math.random() * url.length)];
        }
        var requestString = url;
        var tileRowGroup = "";
        var tileColGroup = "";
        if (newParams.tilerow < 0) {
            tileRowGroup = '-';
        }
        if (newParams.tilerow == 0) {
            tileRowGroup += '0';
        } else {
            tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder;
        }
        if (newParams.tilecol < 0) {
            tileColGroup = '-';
        }
        if (newParams.tilecol == 0) {
            tileColGroup += '0';
        } else {
            tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder;
        }
        var tilePath = '/S' + Math.floor(newParams.scaleindex) + '/' + this.params.basemaplayergroupname + '/R' + tileRowGroup + '/C' + tileColGroup + '/' + (newParams.tilerow % this.params.tileRowsPerFolder) + '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) + '.' + this.params.format;
        if (this.params.querystring) {
            tilePath += "?" + this.params.querystring;
        }
        requestString += tilePath;
        return requestString;
    },
    calculateGridLayout: function (bounds, origin, resolution) {
        var tilelon = resolution * this.tileSize.w;
        var tilelat = resolution * this.tileSize.h;
        var offsetlon = bounds.left - origin.lon;
        var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;
        var tilecolremain = offsetlon / tilelon - tilecol;
        var tileoffsetx = -tilecolremain * this.tileSize.w;
        var tileoffsetlon = origin.lon + tilecol * tilelon;
        var offsetlat = origin.lat - bounds.top + tilelat;
        var tilerow = Math.floor(offsetlat / tilelat) - this.buffer;
        var tilerowremain = tilerow - offsetlat / tilelat;
        var tileoffsety = tilerowremain * this.tileSize.h;
        var tileoffsetlat = origin.lat - tilelat * tilerow;
        return {
            tilelon: tilelon,
            tilelat: tilelat,
            tileoffsetlon: tileoffsetlon,
            tileoffsetlat: tileoffsetlat,
            tileoffsetx: tileoffsetx,
            tileoffsety: tileoffsety
        };
    },
    CLASS_NAME: "OpenLayers.Layer.MapGuide"
});
OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ['measure', 'measurepartial'],
    handlerOptions: null,
    callbacks: null,
    displaySystem: 'metric',
    geodesic: false,
    displaySystemUnits: {
        geographic: ['dd'],
        english: ['mi', 'ft', 'in'],
        metric: ['km', 'm']
    },
    partialDelay: 300,
    delayedTrigger: null,
    persist: false,
    immediate: false,
    initialize: function (handler, options) {
        this.EVENT_TYPES = OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        var callbacks = {
            done: this.measureComplete,
            point: this.measurePartial
        };
        if (this.immediate) {
            callbacks.modify = this.measureImmediate;
        }
        this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
        this.handlerOptions = OpenLayers.Util.extend({
            persist: this.persist
        }, this.handlerOptions);
        this.handler = new handler(this, this.callbacks, this.handlerOptions);
    },
    deactivate: function () {
        this.cancelDelay();
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    cancel: function () {
        this.cancelDelay();
        this.handler.cancel();
    },
    setImmediate: function (immediate) {
        this.immediate = immediate;
        if (this.immediate) {
            this.callbacks.modify = this.measureImmediate;
        } else {
            delete this.callbacks.modify;
        }
    },
    updateHandler: function (handler, options) {
        var active = this.active;
        if (active) {
            this.deactivate();
        }
        this.handler = new handler(this, this.callbacks, options);
        if (active) {
            this.activate();
        }
    },
    measureComplete: function (geometry) {
        this.cancelDelay();
        this.measure(geometry, "measure");
    },
    measurePartial: function (point, geometry) {
        this.cancelDelay();
        geometry = geometry.clone();
        if (this.handler.freehandMode(this.handler.evt)) {
            this.measure(geometry, "measurepartial");
        } else {
            this.delayedTrigger = window.setTimeout(OpenLayers.Function.bind(function () {
                this.delayedTrigger = null;
                this.measure(geometry, "measurepartial");
            }, this), this.partialDelay);
        }
    },
    measureImmediate: function (point, feature, drawing) {
        if (drawing && this.delayedTrigger === null && !this.handler.freehandMode(this.handler.evt)) {
            this.measure(feature.geometry, "measurepartial");
        }
    },
    cancelDelay: function () {
        if (this.delayedTrigger !== null) {
            window.clearTimeout(this.delayedTrigger);
            this.delayedTrigger = null;
        }
    },
    measure: function (geometry, eventType) {
        var stat, order;
        if (geometry.CLASS_NAME.indexOf('LineString') > -1) {
            stat = this.getBestLength(geometry);
            order = 1;
        } else {
            stat = this.getBestArea(geometry);
            order = 2;
        }
        this.events.triggerEvent(eventType, {
            measure: stat[0],
            units: stat[1],
            order: order,
            geometry: geometry
        });
    },
    getBestArea: function (geometry) {
        var units = this.displaySystemUnits[this.displaySystem];
        var unit, area;
        for (var i = 0, len = units.length; i < len; ++i) {
            unit = units[i];
            area = this.getArea(geometry, unit);
            if (area > 1) {
                break;
            }
        }
        return [area, unit];
    },
    getArea: function (geometry, units) {
        var area, geomUnits;
        if (this.geodesic) {
            area = geometry.getGeodesicArea(this.map.getProjectionObject());
            geomUnits = "m";
        } else {
            area = geometry.getArea();
            geomUnits = this.map.getUnits();
        }
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
        if (inPerDisplayUnit) {
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
        }
        return area;
    },
    getBestLength: function (geometry) {
        var units = this.displaySystemUnits[this.displaySystem];
        var unit, length;
        for (var i = 0, len = units.length; i < len; ++i) {
            unit = units[i];
            length = this.getLength(geometry, unit);
            if (length > 1) {
                break;
            }
        }
        return [length, unit];
    },
    getLength: function (geometry, units) {
        var length, geomUnits;
        if (this.geodesic) {
            length = geometry.getGeodesicLength(this.map.getProjectionObject());
            geomUnits = "m";
        } else {
            length = geometry.getLength();
            geomUnits = this.map.getUnits();
        }
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
        if (inPerDisplayUnit) {
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
            length *= (inPerMapUnit / inPerDisplayUnit);
        }
        return length;
    },
    CLASS_NAME: "OpenLayers.Control.Measure"
});
OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    units: null,
    resolution: OpenLayers.DOTS_PER_INCH,
    DEFAULT_PARAMS: {
        i: 'jpeg',
        map: ''
    },
    initialize: function (name, url, params, options) {
        var newArguments = [];
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
        this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS);
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var mapRes = this.map.getResolution();
        var scale = Math.round((this.map.getScale() * 10000)) / 10000;
        var pX = Math.round(bounds.left / mapRes);
        var pY = -Math.round(bounds.top / mapRes);
        return this.getFullRequestString({
            t: pY,
            l: pX,
            s: scale
        });
    },
    calculateGridLayout: function (bounds, origin, resolution) {
        var tilelon = resolution * this.tileSize.w;
        var tilelat = resolution * this.tileSize.h;
        var offsetlon = bounds.left;
        var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;
        var tilecolremain = offsetlon / tilelon - tilecol;
        var tileoffsetx = -tilecolremain * this.tileSize.w;
        var tileoffsetlon = tilecol * tilelon;
        var offsetlat = bounds.top;
        var tilerow = Math.ceil(offsetlat / tilelat) + this.buffer;
        var tilerowremain = tilerow - offsetlat / tilelat;
        var tileoffsety = -(tilerowremain + 1) * this.tileSize.h;
        var tileoffsetlat = tilerow * tilelat;
        return {
            tilelon: tilelon,
            tilelat: tilelat,
            tileoffsetlon: tileoffsetlon,
            tileoffsetlat: tileoffsetlat,
            tileoffsetx: tileoffsetx,
            tileoffsety: tileoffsety
        };
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.KaMap(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        if (this.tileSize != null) {
            obj.tileSize = this.tileSize.clone();
        }
        obj.grid = [];
        return obj;
    },
    getTileBounds: function (viewPortPx) {
        var resolution = this.getResolution();
        var tileMapWidth = resolution * this.tileSize.w;
        var tileMapHeight = resolution * this.tileSize.h;
        var mapPoint = this.getLonLatFromViewPortPx(viewPortPx);
        var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth);
        var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight);
        return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight);
    },
    CLASS_NAME: "OpenLayers.Layer.KaMap"
});
OpenLayers.Popup.Framed = OpenLayers.Class(OpenLayers.Popup.Anchored, {
    imageSrc: null,
    imageSize: null,
    isAlphaImage: false,
    positionBlocks: null,
    blocks: null,
    fixedRelativePosition: false,
    initialize: function (id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {
        OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments);
        if (this.fixedRelativePosition) {
            this.updateRelativePosition();
            this.calculateRelativePosition = function (px) {
                return this.relativePosition;
            };
        }
        this.contentDiv.style.position = "absolute";
        this.contentDiv.style.zIndex = 1;
        if (closeBox) {
            this.closeDiv.style.zIndex = 1;
        }
        this.groupDiv.style.position = "absolute";
        this.groupDiv.style.top = "0px";
        this.groupDiv.style.left = "0px";
        this.groupDiv.style.height = "100%";
        this.groupDiv.style.width = "100%";
    },
    destroy: function () {
        this.imageSrc = null;
        this.imageSize = null;
        this.isAlphaImage = null;
        this.fixedRelativePosition = false;
        this.positionBlocks = null;
        for (var i = 0; i < this.blocks.length; i++) {
            var block = this.blocks[i];
            if (block.image) {
                block.div.removeChild(block.image);
            }
            block.image = null;
            if (block.div) {
                this.groupDiv.removeChild(block.div);
            }
            block.div = null;
        }
        this.blocks = null;
        OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments);
    },
    setBackgroundColor: function (color) {},
    setBorder: function () {},
    setOpacity: function (opacity) {},
    setSize: function (contentSize) {
        OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments);
        this.updateBlocks();
    },
    updateRelativePosition: function () {
        this.padding = this.positionBlocks[this.relativePosition].padding;
        if (this.closeDiv) {
            var contentDivPadding = this.getContentDivPadding();
            this.closeDiv.style.right = contentDivPadding.right + this.padding.right + "px";
            this.closeDiv.style.top = contentDivPadding.top + this.padding.top + "px";
        }
        this.updateBlocks();
    },
    calculateNewPx: function (px) {
        var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply(this, arguments);
        newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset);
        return newPx;
    },
    createBlocks: function () {
        this.blocks = [];
        var firstPosition = null;
        for (var key in this.positionBlocks) {
            firstPosition = key;
            break;
        }
        var position = this.positionBlocks[firstPosition];
        for (var i = 0; i < position.blocks.length; i++) {
            var block = {};
            this.blocks.push(block);
            var divId = this.id + '_FrameDecorationDiv_' + i;
            block.div = OpenLayers.Util.createDiv(divId, null, null, null, "absolute", null, "hidden", null);
            var imgId = this.id + '_FrameDecorationImg_' + i;
            var imageCreator = (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : OpenLayers.Util.createImage;
            block.image = imageCreator(imgId, null, this.imageSize, this.imageSrc, "absolute", null, null, null);
            block.div.appendChild(block.image);
            this.groupDiv.appendChild(block.div);
        }
    },
    updateBlocks: function () {
        if (!this.blocks) {
            this.createBlocks();
        }
        if (this.size && this.relativePosition) {
            var position = this.positionBlocks[this.relativePosition];
            for (var i = 0; i < position.blocks.length; i++) {
                var positionBlock = position.blocks[i];
                var block = this.blocks[i];
                var l = positionBlock.anchor.left;
                var b = positionBlock.anchor.bottom;
                var r = positionBlock.anchor.right;
                var t = positionBlock.anchor.top;
                var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : positionBlock.size.w;
                var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : positionBlock.size.h;
                block.div.style.width = (w < 0 ? 0 : w) + 'px';
                block.div.style.height = (h < 0 ? 0 : h) + 'px';
                block.div.style.left = (l != null) ? l + 'px' : '';
                block.div.style.bottom = (b != null) ? b + 'px' : '';
                block.div.style.right = (r != null) ? r + 'px' : '';
                block.div.style.top = (t != null) ? t + 'px' : '';
                block.image.style.left = positionBlock.position.x + 'px';
                block.image.style.top = positionBlock.position.y + 'px';
            }
            this.contentDiv.style.left = this.padding.left + "px";
            this.contentDiv.style.top = this.padding.top + "px";
        }
    },
    CLASS_NAME: "OpenLayers.Popup.Framed"
});
OpenLayers.Popup.FramedCloud = OpenLayers.Class(OpenLayers.Popup.Framed, {
    contentDisplayClass: "olFramedCloudPopupContent",
    autoSize: true,
    panMapIfOutOfView: true,
    imageSize: new OpenLayers.Size(1276, 736),
    isAlphaImage: false,
    fixedRelativePosition: false,
    positionBlocks: {
        "tl": {
            'offset': new OpenLayers.Pixel(44, 0),
            'padding': new OpenLayers.Bounds(8, 40, 8, 9),
            'blocks': [{
                size: new OpenLayers.Size('auto', 'auto'),
                anchor: new OpenLayers.Bounds(0, 51, 22, 0),
                position: new OpenLayers.Pixel(0, 0)
            }, {
                size: new OpenLayers.Size(22, 'auto'),
                anchor: new OpenLayers.Bounds(null, 50, 0, 0),
                position: new OpenLayers.Pixel(-1238, 0)
            }, {
                size: new OpenLayers.Size('auto', 19),
                anchor: new OpenLayers.Bounds(0, 32, 22, null),
                position: new OpenLayers.Pixel(0, -631)
            }, {
                size: new OpenLayers.Size(22, 18),
                anchor: new OpenLayers.Bounds(null, 32, 0, null),
                position: new OpenLayers.Pixel(-1238, -632)
            }, {
                size: new OpenLayers.Size(81, 35),
                anchor: new OpenLayers.Bounds(null, 0, 0, null),
                position: new OpenLayers.Pixel(0, -688)
            }]
        },
        "tr": {
            'offset': new OpenLayers.Pixel(-45, 0),
            'padding': new OpenLayers.Bounds(8, 40, 8, 9),
            'blocks': [{
                size: new OpenLayers.Size('auto', 'auto'),
                anchor: new OpenLayers.Bounds(0, 51, 22, 0),
                position: new OpenLayers.Pixel(0, 0)
            }, {
                size: new OpenLayers.Size(22, 'auto'),
                anchor: new OpenLayers.Bounds(null, 50, 0, 0),
                position: new OpenLayers.Pixel(-1238, 0)
            }, {
                size: new OpenLayers.Size('auto', 19),
                anchor: new OpenLayers.Bounds(0, 32, 22, null),
                position: new OpenLayers.Pixel(0, -631)
            }, {
                size: new OpenLayers.Size(22, 19),
                anchor: new OpenLayers.Bounds(null, 32, 0, null),
                position: new OpenLayers.Pixel(-1238, -631)
            }, {
                size: new OpenLayers.Size(81, 35),
                anchor: new OpenLayers.Bounds(0, 0, null, null),
                position: new OpenLayers.Pixel(-215, -687)
            }]
        },
        "bl": {
            'offset': new OpenLayers.Pixel(45, 0),
            'padding': new OpenLayers.Bounds(8, 9, 8, 40),
            'blocks': [{
                size: new OpenLayers.Size('auto', 'auto'),
                anchor: new OpenLayers.Bounds(0, 21, 22, 32),
                position: new OpenLayers.Pixel(0, 0)
            }, {
                size: new OpenLayers.Size(22, 'auto'),
                anchor: new OpenLayers.Bounds(null, 21, 0, 32),
                position: new OpenLayers.Pixel(-1238, 0)
            }, {
                size: new OpenLayers.Size('auto', 21),
                anchor: new OpenLayers.Bounds(0, 0, 22, null),
                position: new OpenLayers.Pixel(0, -629)
            }, {
                size: new OpenLayers.Size(22, 21),
                anchor: new OpenLayers.Bounds(null, 0, 0, null),
                position: new OpenLayers.Pixel(-1238, -629)
            }, {
                size: new OpenLayers.Size(81, 33),
                anchor: new OpenLayers.Bounds(null, null, 0, 0),
                position: new OpenLayers.Pixel(-101, -674)
            }]
        },
        "br": {
            'offset': new OpenLayers.Pixel(-44, 0),
            'padding': new OpenLayers.Bounds(8, 9, 8, 40),
            'blocks': [{
                size: new OpenLayers.Size('auto', 'auto'),
                anchor: new OpenLayers.Bounds(0, 21, 22, 32),
                position: new OpenLayers.Pixel(0, 0)
            }, {
                size: new OpenLayers.Size(22, 'auto'),
                anchor: new OpenLayers.Bounds(null, 21, 0, 32),
                position: new OpenLayers.Pixel(-1238, 0)
            }, {
                size: new OpenLayers.Size('auto', 21),
                anchor: new OpenLayers.Bounds(0, 0, 22, null),
                position: new OpenLayers.Pixel(0, -629)
            }, {
                size: new OpenLayers.Size(22, 21),
                anchor: new OpenLayers.Bounds(null, 0, 0, null),
                position: new OpenLayers.Pixel(-1238, -629)
            }, {
                size: new OpenLayers.Size(81, 33),
                anchor: new OpenLayers.Bounds(0, null, null, 0),
                position: new OpenLayers.Pixel(-311, -674)
            }]
        }
    },
    minSize: new OpenLayers.Size(105, 10),
    maxSize: new OpenLayers.Size(1200, 660),
    initialize: function (id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) {
        this.imageSrc = OpenLayers.Util.getImagesLocation() + 'cloud-popup-relative.png';
        OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments);
        this.contentDiv.className = this.contentDisplayClass;
    },
    destroy: function () {
        OpenLayers.Popup.Framed.prototype.destroy.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Popup.FramedCloud"
});
OpenLayers.Tile.Image.IFrame = {
    useIFrame: null,
    clear: function () {
        if (this.useIFrame) {
            if (this.imgDiv) {
                var iFrame = this.imgDiv.firstChild;
                OpenLayers.Event.stopObservingElement(iFrame);
                this.imgDiv.removeChild(iFrame);
                delete iFrame;
            }
        } else {
            OpenLayers.Tile.Image.prototype.clear.apply(this, arguments);
        }
    },
    renderTile: function () {
        if (OpenLayers.Tile.Image.prototype.renderTile.apply(this, arguments) && this.useIFrame) {
            var form = this.createRequestForm();
            this.imgDiv.appendChild(form);
            form.submit();
            this.imgDiv.removeChild(form);
            delete form;
        }
        return true;
    },
    initImgDiv: function () {
        this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && this.url.length > this.maxGetUrlLength;
        if (this.imgDiv != null) {
            var nodeName = this.imgDiv.nodeName.toLowerCase();
            if ((this.useIFrame && nodeName == "img") || (!this.useIFrame && nodeName == "div")) {
                this.removeImgDiv();
                this.imgDiv = null;
            }
        }
        if (this.useIFrame) {
            if (this.imgDiv == null) {
                var eventPane = document.createElement("div");
                if (OpenLayers.BROWSER_NAME == "msie") {
                    eventPane.style.backgroundColor = '#FFFFFF';
                    eventPane.style.filter = 'chroma(color=#FFFFFF)';
                }
                OpenLayers.Util.modifyDOMElement(eventPane, null, new OpenLayers.Pixel(0, 0), this.layer.getImageSize(), "absolute");
                this.imgDiv = document.createElement("div");
                this.imgDiv.appendChild(eventPane);
                OpenLayers.Util.modifyDOMElement(this.imgDiv, this.id, null, this.layer.getImageSize(), "relative");
                this.imgDiv.className = 'olTileImage';
                this.frame.appendChild(this.imgDiv);
                this.layer.div.appendChild(this.frame);
                if (this.layer.opacity != null) {
                    OpenLayers.Util.modifyDOMElement(this.imgDiv, null, null, null, null, null, null, this.layer.opacity);
                }
                this.imgDiv.map = this.layer.map;
            }
            this.imgDiv.viewRequestID = this.layer.map.viewRequestID;
        } else {
            OpenLayers.Tile.Image.prototype.initImgDiv.apply(this, arguments);
        }
    },
    createIFrame: function () {
        var id = this.id + '_iFrame';
        var iframe;
        if (OpenLayers.BROWSER_NAME == "msie") {
            iframe = document.createElement('<iframe name="' + id + '">');
            iframe.style.backgroundColor = '#FFFFFF';
            iframe.style.filter = 'chroma(color=#FFFFFF)';
        } else {
            iframe = document.createElement('iframe');
            iframe.style.backgroundColor = 'transparent';
            iframe.name = id;
        }
        iframe.id = id;
        iframe.scrolling = 'no';
        iframe.marginWidth = '0px';
        iframe.marginHeight = '0px';
        iframe.frameBorder = '0';
        OpenLayers.Util.modifyDOMElement(iframe, id, new OpenLayers.Pixel(0, 0), this.layer.getImageSize(), "absolute");
        var onload = function () {
                if (this.isLoading) {
                    this.isLoading = false;
                    this.events.triggerEvent("loadend");
                }
            };
        OpenLayers.Event.observe(iframe, 'load', OpenLayers.Function.bind(onload, this));
        return iframe;
    },
    createRequestForm: function () {
        var form = document.createElement('form');
        form.method = 'POST';
        var cacheId = this.layer.params["_OLSALT"];
        cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX();
        form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId);
        this.imgDiv.insertBefore(this.createIFrame(), this.imgDiv.firstChild);
        form.target = this.id + '_iFrame';
        var imageSize = this.layer.getImageSize();
        var params = OpenLayers.Util.getParameters(this.url);
        for (var par in params) {
            var field = document.createElement('input');
            field.type = 'hidden';
            field.name = par;
            field.value = params[par];
            form.appendChild(field);
        }
        return form;
    }
};
OpenLayers.Geometry.Rectangle = OpenLayers.Class(OpenLayers.Geometry, {
    x: null,
    y: null,
    width: null,
    height: null,
    initialize: function (x, y, width, height) {
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    },
    calculateBounds: function () {
        this.bounds = new OpenLayers.Bounds(this.x, this.y, this.x + this.width, this.y + this.height);
    },
    getLength: function () {
        var length = (2 * this.width) + (2 * this.height);
        return length;
    },
    getArea: function () {
        var area = this.width * this.height;
        return area;
    },
    CLASS_NAME: "OpenLayers.Geometry.Rectangle"
});
OpenLayers.Tile.WFS = OpenLayers.Class(OpenLayers.Tile, {
    features: null,
    url: null,
    request: null,
    initialize: function (layer, position, bounds, url, size) {
        OpenLayers.Tile.prototype.initialize.apply(this, arguments);
        this.url = url;
        this.features = [];
    },
    destroy: function () {
        OpenLayers.Tile.prototype.destroy.apply(this, arguments);
        this.destroyAllFeatures();
        this.features = null;
        this.url = null;
        if (this.request) {
            this.request.abort();
            this.request = null;
        }
    },
    clear: function () {
        this.destroyAllFeatures();
    },
    draw: function () {
        if (OpenLayers.Tile.prototype.draw.apply(this, arguments)) {
            if (this.isLoading) {
                this.events.triggerEvent("reload");
            } else {
                this.isLoading = true;
                this.events.triggerEvent("loadstart");
            }
            this.loadFeaturesForRegion(this.requestSuccess);
        }
    },
    loadFeaturesForRegion: function (success, failure) {
        if (this.request) {
            this.request.abort();
        }
        this.request = OpenLayers.Request.GET({
            url: this.url,
            success: success,
            failure: failure,
            scope: this
        });
    },
    requestSuccess: function (request) {
        if (this.features) {
            var doc = request.responseXML;
            if (!doc || !doc.documentElement) {
                doc = request.responseText;
            }
            if (this.layer.vectorMode) {
                this.layer.addFeatures(this.layer.formatObject.read(doc));
            } else {
                var xml = new OpenLayers.Format.XML();
                if (typeof doc == "string") {
                    doc = xml.read(doc);
                }
                var resultFeatures = xml.getElementsByTagNameNS(doc, "http://www.opengis.net/gml", "featureMember");
                this.addResults(resultFeatures);
            }
        }
        if (this.events) {
            this.events.triggerEvent("loadend");
        }
        this.request = null;
    },
    addResults: function (results) {
        for (var i = 0; i < results.length; i++) {
            var feature = new this.layer.featureClass(this.layer, results[i]);
            this.features.push(feature);
        }
    },
    destroyAllFeatures: function () {
        while (this.features.length > 0) {
            var feature = this.features.shift();
            feature.destroy();
        }
    },
    CLASS_NAME: "OpenLayers.Tile.WFS"
});
OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    CLASS_NAME: "OpenLayers.Format.SOSCapabilities"
});
OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.SOSCapabilities, {
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        sos: "http://www.opengis.net/sos/1.0",
        gml: "http://www.opengis.net/gml",
        xlink: "http://www.w3.org/1999/xlink"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        this.options = options;
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var capabilities = {};
        this.readNode(data, capabilities);
        return capabilities;
    },
    readers: {
        "gml": OpenLayers.Util.applyDefaults({
            "name": function (node, obj) {
                obj.name = this.getChildValue(node);
            },
            "TimePeriod": function (node, obj) {
                obj.timePeriod = {};
                this.readChildNodes(node, obj.timePeriod);
            },
            "beginPosition": function (node, timePeriod) {
                timePeriod.beginPosition = this.getChildValue(node);
            },
            "endPosition": function (node, timePeriod) {
                timePeriod.endPosition = this.getChildValue(node);
            }
        }, OpenLayers.Format.GML.v3.prototype.readers["gml"]),
        "sos": {
            "Capabilities": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Contents": function (node, obj) {
                obj.contents = {};
                this.readChildNodes(node, obj.contents);
            },
            "ObservationOfferingList": function (node, contents) {
                contents.offeringList = {};
                this.readChildNodes(node, contents.offeringList);
            },
            "ObservationOffering": function (node, offeringList) {
                var id = this.getAttributeNS(node, this.namespaces.gml, "id");
                offeringList[id] = {
                    procedures: [],
                    observedProperties: [],
                    featureOfInterestIds: [],
                    responseFormats: [],
                    resultModels: [],
                    responseModes: []
                };
                this.readChildNodes(node, offeringList[id]);
            },
            "time": function (node, offering) {
                offering.time = {};
                this.readChildNodes(node, offering.time);
            },
            "procedure": function (node, offering) {
                offering.procedures.push(this.getAttributeNS(node, this.namespaces.xlink, "href"));
            },
            "observedProperty": function (node, offering) {
                offering.observedProperties.push(this.getAttributeNS(node, this.namespaces.xlink, "href"));
            },
            "featureOfInterest": function (node, offering) {
                offering.featureOfInterestIds.push(this.getAttributeNS(node, this.namespaces.xlink, "href"));
            },
            "responseFormat": function (node, offering) {
                offering.responseFormats.push(this.getChildValue(node));
            },
            "resultModel": function (node, offering) {
                offering.resultModels.push(this.getChildValue(node));
            },
            "responseMode": function (node, offering) {
                offering.responseModes.push(this.getChildValue(node));;
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
    },
    CLASS_NAME: "OpenLayers.Format.SOSCapabilities.v1_0_0"
});
OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, {
    started: false,
    stopDown: false,
    pinching: false,
    last: null,
    start: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },
    touchstart: function (evt) {
        var propagate = true;
        this.pinching = false;
        if (OpenLayers.Event.isMultiTouch(evt)) {
            this.started = true;
            this.last = this.start = {
                distance: this.getDistance(evt.touches),
                delta: 0,
                scale: 1
            };
            this.callback("start", [evt, this.start]);
            propagate = !this.stopDown;
        } else {
            this.started = false;
            this.start = null;
            this.last = null;
        }
        OpenLayers.Event.stop(evt);
        return propagate;
    },
    touchmove: function (evt) {
        if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
            this.pinching = true;
            var current = this.getPinchData(evt);
            this.callback("move", [evt, current]);
            this.last = current;
            OpenLayers.Event.stop(evt);
        }
        return true;
    },
    touchend: function (evt) {
        if (this.started) {
            this.started = false;
            this.pinching = false;
            this.callback("done", [evt, this.start, this.last]);
            this.start = null;
            this.last = null;
        }
        return true;
    },
    activate: function () {
        var activated = false;
        if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) {
            this.pinching = false;
            activated = true;
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.started = false;
            this.pinching = false;
            this.start = null;
            this.last = null;
            deactivated = true;
        }
        return deactivated;
    },
    getDistance: function (touches) {
        var t0 = touches[0];
        var t1 = touches[1];
        return Math.sqrt(Math.pow(t0.clientX - t1.clientX, 2) + Math.pow(t0.clientY - t1.clientY, 2));
    },
    getPinchData: function (evt) {
        var distance = this.getDistance(evt.touches);
        var scale = distance / this.start.distance;
        return {
            distance: distance,
            delta: this.last.distance - distance,
            scale: scale
        };
    },
    CLASS_NAME: "OpenLayers.Handler.Pinch"
});
OpenLayers.Control.MouseDefaults = OpenLayers.Class(OpenLayers.Control, {
    performedDrag: false,
    wheelObserver: null,
    initialize: function () {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },
    destroy: function () {
        if (this.handler) {
            this.handler.destroy();
        }
        this.handler = null;
        this.map.events.un({
            "click": this.defaultClick,
            "dblclick": this.defaultDblClick,
            "mousedown": this.defaultMouseDown,
            "mouseup": this.defaultMouseUp,
            "mousemove": this.defaultMouseMove,
            "mouseout": this.defaultMouseOut,
            scope: this
        });
        OpenLayers.Event.stopObserving(window, "DOMMouseScroll", this.wheelObserver);
        OpenLayers.Event.stopObserving(window, "mousewheel", this.wheelObserver);
        OpenLayers.Event.stopObserving(document, "mousewheel", this.wheelObserver);
        this.wheelObserver = null;
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    draw: function () {
        this.map.events.on({
            "click": this.defaultClick,
            "dblclick": this.defaultDblClick,
            "mousedown": this.defaultMouseDown,
            "mouseup": this.defaultMouseUp,
            "mousemove": this.defaultMouseMove,
            "mouseout": this.defaultMouseOut,
            scope: this
        });
        this.registerWheelEvents();
    },
    registerWheelEvents: function () {
        this.wheelObserver = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this);
        OpenLayers.Event.observe(window, "DOMMouseScroll", this.wheelObserver);
        OpenLayers.Event.observe(window, "mousewheel", this.wheelObserver);
        OpenLayers.Event.observe(document, "mousewheel", this.wheelObserver);
    },
    defaultClick: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        var notAfterDrag = !this.performedDrag;
        this.performedDrag = false;
        return notAfterDrag;
    },
    defaultDblClick: function (evt) {
        var newCenter = this.map.getLonLatFromViewPortPx(evt.xy);
        this.map.setCenter(newCenter, this.map.zoom + 1);
        OpenLayers.Event.stop(evt);
        return false;
    },
    defaultMouseDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        this.mouseDragStart = evt.xy.clone();
        this.performedDrag = false;
        if (evt.shiftKey) {
            this.map.div.style.cursor = "crosshair";
            this.zoomBox = OpenLayers.Util.createDiv('zoomBox', this.mouseDragStart, null, null, "absolute", "2px solid red");
            this.zoomBox.style.backgroundColor = "white";
            this.zoomBox.style.filter = "alpha(opacity=50)";
            this.zoomBox.style.opacity = "0.50";
            this.zoomBox.style.fontSize = "1px";
            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
            this.map.eventsDiv.appendChild(this.zoomBox);
        }
        document.onselectstart = OpenLayers.Function.False;
        OpenLayers.Event.stop(evt);
    },
    defaultMouseMove: function (evt) {
        this.mousePosition = evt.xy.clone();
        if (this.mouseDragStart != null) {
            if (this.zoomBox) {
                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
                if (evt.xy.x < this.mouseDragStart.x) {
                    this.zoomBox.style.left = evt.xy.x + "px";
                }
                if (evt.xy.y < this.mouseDragStart.y) {
                    this.zoomBox.style.top = evt.xy.y + "px";
                }
            } else {
                var deltaX = this.mouseDragStart.x - evt.xy.x;
                var deltaY = this.mouseDragStart.y - evt.xy.y;
                var size = this.map.getSize();
                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX, size.h / 2 + deltaY);
                var newCenter = this.map.getLonLatFromViewPortPx(newXY);
                this.map.setCenter(newCenter, null, true);
                this.mouseDragStart = evt.xy.clone();
                this.map.div.style.cursor = "move";
            }
            this.performedDrag = true;
        }
    },
    defaultMouseUp: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        if (this.zoomBox) {
            this.zoomBoxEnd(evt);
        } else {
            if (this.performedDrag) {
                this.map.setCenter(this.map.center);
            }
        }
        document.onselectstart = null;
        this.mouseDragStart = null;
        this.map.div.style.cursor = "";
    },
    defaultMouseOut: function (evt) {
        if (this.mouseDragStart != null && OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
            if (this.zoomBox) {
                this.removeZoomBox();
            }
            this.mouseDragStart = null;
        }
    },
    defaultWheelUp: function (evt) {
        if (this.map.getZoom() <= this.map.getNumZoomLevels()) {
            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy), this.map.getZoom() + 1);
        }
    },
    defaultWheelDown: function (evt) {
        if (this.map.getZoom() > 0) {
            this.map.setCenter(this.map.getLonLatFromPixel(evt.xy), this.map.getZoom() - 1);
        }
    },
    zoomBoxEnd: function (evt) {
        if (this.mouseDragStart != null) {
            if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 || Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {
                var start = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
                var end = this.map.getLonLatFromViewPortPx(evt.xy);
                var top = Math.max(start.lat, end.lat);
                var bottom = Math.min(start.lat, end.lat);
                var left = Math.min(start.lon, end.lon);
                var right = Math.max(start.lon, end.lon);
                var bounds = new OpenLayers.Bounds(left, bottom, right, top);
                this.map.zoomToExtent(bounds);
            } else {
                var end = this.map.getLonLatFromViewPortPx(evt.xy);
                this.map.setCenter(new OpenLayers.LonLat((end.lon), (end.lat)), this.map.getZoom() + 1);
            }
            this.removeZoomBox();
        }
    },
    removeZoomBox: function () {
        this.map.eventsDiv.removeChild(this.zoomBox);
        this.zoomBox = null;
    },
    onWheelEvent: function (e) {
        var inMap = false;
        var elem = OpenLayers.Event.element(e);
        while (elem != null) {
            if (this.map && elem == this.map.div) {
                inMap = true;
                break;
            }
            elem = elem.parentNode;
        }
        if (inMap) {
            var delta = 0;
            if (!e) {
                e = window.event;
            }
            if (e.wheelDelta) {
                delta = e.wheelDelta / 120;
                if (window.opera && window.opera.version() < 9.2) {
                    delta = -delta;
                }
            } else if (e.detail) {
                delta = -e.detail / 3;
            }
            if (delta) {
                e.xy = this.mousePosition;
                if (delta < 0) {
                    this.defaultWheelDown(e);
                } else {
                    this.defaultWheelUp(e);
                }
            }
            OpenLayers.Event.stop(e);
        }
    },
    CLASS_NAME: "OpenLayers.Control.MouseDefaults"
});
OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        xsd: "http://www.w3.org/2001/XMLSchema"
    },
    readers: {
        "xsd": {
            "schema": function (node, obj) {
                var complexTypes = [];
                var customTypes = {};
                var schema = {
                    complexTypes: complexTypes,
                    customTypes: customTypes
                };
                this.readChildNodes(node, schema);
                var attributes = node.attributes;
                var attr, name;
                for (var i = 0, len = attributes.length; i < len; ++i) {
                    attr = attributes[i];
                    name = attr.name;
                    if (name.indexOf("xmlns") == 0) {
                        this.setNamespace(name.split(":")[1] || "", attr.value);
                    } else {
                        obj[name] = attr.value;
                    }
                }
                obj.featureTypes = complexTypes;
                obj.targetPrefix = this.namespaceAlias[obj.targetNamespace];
                var complexType, customType;
                for (var i = 0, len = complexTypes.length; i < len; ++i) {
                    complexType = complexTypes[i];
                    customType = customTypes[complexType.typeName];
                    if (customTypes[complexType.typeName]) {
                        complexType.typeName = customType.name;
                    }
                }
            },
            "complexType": function (node, obj) {
                var complexType = {
                    "typeName": node.getAttribute("name")
                };
                this.readChildNodes(node, complexType);
                obj.complexTypes.push(complexType);
            },
            "complexContent": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "extension": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "sequence": function (node, obj) {
                var sequence = {
                    elements: []
                };
                this.readChildNodes(node, sequence);
                obj.properties = sequence.elements;
            },
            "element": function (node, obj) {
                if (obj.elements) {
                    var element = {};
                    var attributes = node.attributes;
                    var attr;
                    for (var i = 0, len = attributes.length; i < len; ++i) {
                        attr = attributes[i];
                        element[attr.name] = attr.value;
                    }
                    var type = element.type;
                    if (!type) {
                        type = {};
                        this.readChildNodes(node, type);
                        element.restriction = type;
                        element.type = type.base;
                    }
                    var fullType = type.base || type;
                    element.localType = fullType.split(":").pop();
                    obj.elements.push(element);
                }
                if (obj.complexTypes) {
                    var type = node.getAttribute("type");
                    var localType = type.split(":").pop();
                    obj.customTypes[localType] = {
                        "name": node.getAttribute("name"),
                        "type": type
                    };
                }
            },
            "simpleType": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "restriction": function (node, obj) {
                obj.base = node.getAttribute("base");
                this.readRestriction(node, obj);
            }
        }
    },
    readRestriction: function (node, obj) {
        var children = node.childNodes;
        var child, nodeName, value;
        for (var i = 0, len = children.length; i < len; ++i) {
            child = children[i];
            if (child.nodeType == 1) {
                nodeName = child.nodeName.split(":").pop();
                value = child.getAttribute("value");
                if (!obj[nodeName]) {
                    obj[nodeName] = value;
                } else {
                    if (typeof obj[nodeName] == "string") {
                        obj[nodeName] = [obj[nodeName]];
                    }
                    obj[nodeName].push(value);
                }
            }
        }
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var schema = {};
        this.readNode(data, schema);
        return schema;
    },
    CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType"
});
OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, {
    force: false,
    interval: 0,
    timer: null,
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.call(this);
        if (activated) {
            if (this.layer.visibility === true) {
                this.start();
            }
            this.layer.events.on({
                "visibilitychanged": this.reset,
                scope: this
            });
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            this.stop();
        }
        return deactivated;
    },
    reset: function () {
        if (this.layer.visibility === true) {
            this.start();
        } else {
            this.stop();
        }
    },
    start: function () {
        if (this.interval && typeof this.interval === "number" && this.interval > 0) {
            this.timer = window.setInterval(OpenLayers.Function.bind(this.refresh, this), this.interval);
        }
    },
    refresh: function () {
        if (this.layer && this.layer.refresh && typeof this.layer.refresh == "function") {
            this.layer.refresh({
                force: this.force
            });
        }
    },
    stop: function () {
        if (this.timer !== null) {
            window.clearInterval(this.timer);
            this.timer = null;
        }
    },
    CLASS_NAME: "OpenLayers.Strategy.Refresh"
});
OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, {
    DEFAULT_PARAMS: {
        format: "png"
    },
    isBaseLayer: true,
    initialize: function (name, url, params, options) {
        var newArguments = [];
        params = OpenLayers.Util.upperCaseObject(params);
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
        OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));
        if (this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") {
            if ((options == null) || (!options.isBaseLayer)) {
                this.isBaseLayer = false;
            }
            if (this.params.FORMAT == "jpg") {
                this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif" : "png";
            }
        }
    },
    destroy: function () {
        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var projWords = this.projection.getCode().split(":");
        var srid = projWords[projWords.length - 1];
        var imageSize = this.getImageSize();
        var newParams = {
            'BBOX': bounds.toBBOX(),
            'SIZE': imageSize.w + "," + imageSize.h,
            'F': "image",
            'BBOXSR': srid,
            'IMAGESR': srid
        };
        if (this.layerDefs) {
            var layerDefStrList = [];
            var layerID;
            for (layerID in this.layerDefs) {
                if (this.layerDefs.hasOwnProperty(layerID)) {
                    if (this.layerDefs[layerID]) {
                        layerDefStrList.push(layerID);
                        layerDefStrList.push(":");
                        layerDefStrList.push(this.layerDefs[layerID]);
                        layerDefStrList.push(";");
                    }
                }
            }
            if (layerDefStrList.length > 0) {
                newParams['LAYERDEFS'] = layerDefStrList.join("");
            }
        }
        var requestString = this.getFullRequestString(newParams);
        return requestString;
    },
    setLayerFilter: function (id, queryDef) {
        if (!this.layerDefs) {
            this.layerDefs = {};
        }
        if (queryDef) {
            this.layerDefs[id] = queryDef;
        } else {
            delete this.layerDefs[id];
        }
    },
    clearLayerFilter: function (id) {
        if (id) {
            delete this.layerDefs[id];
        } else {
            delete this.layerDefs;
        }
    },
    mergeNewParams: function (newParams) {
        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
        var newArguments = [upperParams];
        return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments);
    },
    CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest"
});
OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, {
    DEFAULT_PARAMS: {
        mode: "map",
        map_imagetype: "png"
    },
    initialize: function (name, url, params, options) {
        var newArguments = [];
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
        this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS);
        if (options == null || options.isBaseLayer == null) {
            this.isBaseLayer = ((this.params.transparent != "true") && (this.params.transparent != true));
        }
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.MapServer(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top];
        var imageSize = this.getImageSize();
        var url = this.getFullRequestString({
            mapext: extent,
            imgext: extent,
            map_size: [imageSize.w, imageSize.h],
            imgx: imageSize.w / 2,
            imgy: imageSize.h / 2,
            imgxy: [imageSize.w, imageSize.h]
        });
        return url;
    },
    getFullRequestString: function (newParams, altUrl) {
        var url = (altUrl == null) ? this.url : altUrl;
        var allParams = OpenLayers.Util.extend({}, this.params);
        allParams = OpenLayers.Util.extend(allParams, newParams);
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(paramsString, url);
        }
        var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url));
        for (var key in allParams) {
            if (key.toUpperCase() in urlParams) {
                delete allParams[key];
            }
        }
        paramsString = OpenLayers.Util.getParameterString(allParams);
        var requestString = url;
        paramsString = paramsString.replace(/,/g, "+");
        if (paramsString != "") {
            var lastServerChar = url.charAt(url.length - 1);
            if ((lastServerChar == "&") || (lastServerChar == "?")) {
                requestString += paramsString;
            } else {
                if (url.indexOf('?') == -1) {
                    requestString += '?' + paramsString;
                } else {
                    requestString += '&' + paramsString;
                }
            }
        }
        return requestString;
    },
    CLASS_NAME: "OpenLayers.Layer.MapServer"
});
OpenLayers.Layer.MapServer.Untiled = OpenLayers.Class(OpenLayers.Layer.MapServer, {
    singleTile: true,
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.MapServer.prototype.initialize.apply(this, arguments);
        var msg = "The OpenLayers.Layer.MapServer.Untiled class is deprecated and " + "will be removed in 3.0. Instead, you should use the " + "normal OpenLayers.Layer.MapServer class, passing it the option " + "'singleTile' as true.";
        OpenLayers.Console.warn(msg);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.MapServer.Untiled(this.name, this.url, this.params, this.getOptions());
        }
        obj = OpenLayers.Layer.MapServer.prototype.clone.apply(this, [obj]);
        return obj;
    },
    CLASS_NAME: "OpenLayers.Layer.MapServer.Untiled"
});
OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, {
    delay: 500,
    pixelTolerance: null,
    stopMove: false,
    px: null,
    timerId: null,
    initialize: function (control, callbacks, options) {
        OpenLayers.Handler.prototype.initialize.apply(this, arguments);
    },
    mousemove: function (evt) {
        if (this.passesTolerance(evt.xy)) {
            this.clearTimer();
            this.callback('move', [evt]);
            this.px = evt.xy;
            evt = OpenLayers.Util.extend({}, evt);
            this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay);
        }
        return !this.stopMove;
    },
    mouseout: function (evt) {
        if (OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
            this.clearTimer();
            this.callback('move', [evt]);
        }
        return true;
    },
    passesTolerance: function (px) {
        var passes = true;
        if (this.pixelTolerance && this.px) {
            var dpx = Math.sqrt(Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2));
            if (dpx < this.pixelTolerance) {
                passes = false;
            }
        }
        return passes;
    },
    clearTimer: function () {
        if (this.timerId != null) {
            window.clearTimeout(this.timerId);
            this.timerId = null;
        }
    },
    delayedCall: function (evt) {
        this.callback('pause', [evt]);
    },
    deactivate: function () {
        var deactivated = false;
        if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
            this.clearTimer();
            deactivated = true;
        }
        return deactivated;
    },
    CLASS_NAME: "OpenLayers.Handler.Hover"
});
OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, {
    protocol: null,
    multipleKey: null,
    toggleKey: null,
    modifiers: null,
    multiple: false,
    click: true,
    single: true,
    clickout: true,
    toggle: false,
    clickTolerance: 5,
    hover: false,
    box: false,
    maxFeatures: 10,
    features: null,
    hoverFeature: null,
    handlerOptions: null,
    handlers: null,
    hoverResponse: null,
    filterType: OpenLayers.Filter.Spatial.BBOX,
    EVENT_TYPES: ["featureselected", "featuresselected", "featureunselected", "clickout", "beforefeatureselected", "beforefeaturesselected", "hoverfeature", "outfeature"],
    initialize: function (options) {
        this.EVENT_TYPES = OpenLayers.Control.GetFeature.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        options.handlerOptions = options.handlerOptions || {};
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.features = {};
        this.handlers = {};
        if (this.click) {
            this.handlers.click = new OpenLayers.Handler.Click(this, {
                click: this.selectClick
            }, this.handlerOptions.click || {});
        }
        if (this.box) {
            this.handlers.box = new OpenLayers.Handler.Box(this, {
                done: this.selectBox
            }, OpenLayers.Util.extend(this.handlerOptions.box, {
                boxDivClassName: "olHandlerBoxSelectFeature"
            }));
        }
        if (this.hover) {
            this.handlers.hover = new OpenLayers.Handler.Hover(this, {
                'move': this.cancelHover,
                'pause': this.selectHover
            }, OpenLayers.Util.extend(this.handlerOptions.hover, {
                'delay': 250
            }));
        }
    },
    activate: function () {
        if (!this.active) {
            for (var i in this.handlers) {
                this.handlers[i].activate();
            }
        }
        return OpenLayers.Control.prototype.activate.apply(this, arguments);
    },
    deactivate: function () {
        if (this.active) {
            for (var i in this.handlers) {
                this.handlers[i].deactivate();
            }
        }
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    selectClick: function (evt) {
        var bounds = this.pixelToBounds(evt.xy);
        this.setModifiers(evt);
        this.request(bounds, {
            single: this.single
        });
    },
    selectBox: function (position) {
        var bounds;
        if (position instanceof OpenLayers.Bounds) {
            var minXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.left, position.bottom));
            var maxXY = this.map.getLonLatFromPixel(new OpenLayers.Pixel(position.right, position.top));
            bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat);
        } else {
            if (this.click) {
                return;
            }
            bounds = this.pixelToBounds(position);
        }
        this.setModifiers(this.handlers.box.dragHandler.evt);
        this.request(bounds);
    },
    selectHover: function (evt) {
        var bounds = this.pixelToBounds(evt.xy);
        this.request(bounds, {
            single: true,
            hover: true
        });
    },
    cancelHover: function () {
        if (this.hoverResponse) {
            this.protocol.abort(this.hoverResponse);
            this.hoverResponse = null;
            OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
        }
    },
    request: function (bounds, options) {
        options = options || {};
        var filter = new OpenLayers.Filter.Spatial({
            type: this.filterType,
            value: bounds
        });
        OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
        var response = this.protocol.read({
            maxFeatures: options.single == true ? this.maxFeatures : undefined,
            filter: filter,
            callback: function (result) {
                if (result.success()) {
                    if (result.features.length) {
                        if (options.single == true) {
                            this.selectBestFeature(result.features, bounds.getCenterLonLat(), options);
                        } else {
                            this.select(result.features);
                        }
                    } else if (options.hover) {
                        this.hoverSelect();
                    } else {
                        this.events.triggerEvent("clickout");
                        if (this.clickout) {
                            this.unselectAll();
                        }
                    }
                }
                OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
            },
            scope: this
        });
        if (options.hover == true) {
            this.hoverResponse = response;
        }
    },
    selectBestFeature: function (features, clickPosition, options) {
        options = options || {};
        if (features.length) {
            var point = new OpenLayers.Geometry.Point(clickPosition.lon, clickPosition.lat);
            var feature, resultFeature, dist;
            var minDist = Number.MAX_VALUE;
            for (var i = 0; i < features.length; ++i) {
                feature = features[i];
                if (feature.geometry) {
                    dist = point.distanceTo(feature.geometry, {
                        edge: false
                    });
                    if (dist < minDist) {
                        minDist = dist;
                        resultFeature = feature;
                        if (minDist == 0) {
                            break;
                        }
                    }
                }
            }
            if (options.hover == true) {
                this.hoverSelect(resultFeature);
            } else {
                this.select(resultFeature || features);
            }
        }
    },
    setModifiers: function (evt) {
        this.modifiers = {
            multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]),
            toggle: this.toggle || (this.toggleKey && evt[this.toggleKey])
        };
    },
    select: function (features) {
        if (!this.modifiers.multiple && !this.modifiers.toggle) {
            this.unselectAll();
        }
        if (!(OpenLayers.Util.isArray(features))) {
            features = [features];
        }
        var cont = this.events.triggerEvent("beforefeaturesselected", {
            features: features
        });
        if (cont !== false) {
            var selectedFeatures = [];
            var feature;
            for (var i = 0, len = features.length; i < len; ++i) {
                feature = features[i];
                if (this.features[feature.fid || feature.id]) {
                    if (this.modifiers.toggle) {
                        this.unselect(this.features[feature.fid || feature.id]);
                    }
                } else {
                    cont = this.events.triggerEvent("beforefeatureselected", {
                        feature: feature
                    });
                    if (cont !== false) {
                        this.features[feature.fid || feature.id] = feature;
                        selectedFeatures.push(feature);
                        this.events.triggerEvent("featureselected", {
                            feature: feature
                        });
                    }
                }
            }
            this.events.triggerEvent("featuresselected", {
                features: selectedFeatures
            });
        }
    },
    hoverSelect: function (feature) {
        var fid = feature ? feature.fid || feature.id : null;
        var hfid = this.hoverFeature ? this.hoverFeature.fid || this.hoverFeature.id : null;
        if (hfid && hfid != fid) {
            this.events.triggerEvent("outfeature", {
                feature: this.hoverFeature
            });
            this.hoverFeature = null;
        }
        if (fid && fid != hfid) {
            this.events.triggerEvent("hoverfeature", {
                feature: feature
            });
            this.hoverFeature = feature;
        }
    },
    unselect: function (feature) {
        delete this.features[feature.fid || feature.id];
        this.events.triggerEvent("featureunselected", {
            feature: feature
        });
    },
    unselectAll: function () {
        for (var fid in this.features) {
            this.unselect(this.features[fid]);
        }
    },
    setMap: function (map) {
        for (var i in this.handlers) {
            this.handlers[i].setMap(map);
        }
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
    },
    pixelToBounds: function (pixel) {
        var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2);
        var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2);
        var ll = this.map.getLonLatFromPixel(llPx);
        var ur = this.map.getLonLatFromPixel(urPx);
        return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat);
    },
    CLASS_NAME: "OpenLayers.Control.GetFeature"
});
OpenLayers.Format.QueryStringFilter = (function () {
    var cmpToStr = {};
    cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq";
    cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne";
    cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt";
    cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte";
    cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt";
    cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
    cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike";

    function regex2value(value) {
        value = value.replace(/%/g, "\\%");
        value = value.replace(/\\\\\.(\*)?/g, function ($0, $1) {
            return $1 ? $0 : "\\\\_";
        });
        value = value.replace(/\\\\\.\*/g, "\\\\%");
        value = value.replace(/(\\)?\.(\*)?/g, function ($0, $1, $2) {
            return $1 || $2 ? $0 : "_";
        });
        value = value.replace(/(\\)?\.\*/g, function ($0, $1) {
            return $1 ? $0 : "%";
        });
        value = value.replace(/\\\./g, ".");
        value = value.replace(/(\\)?\\\*/g, function ($0, $1) {
            return $1 ? $0 : "*";
        });
        return value;
    }
    return OpenLayers.Class(OpenLayers.Format, {
        wildcarded: false,
        srsInBBOX: false,
        write: function (filter, params) {
            params = params || {};
            var className = filter.CLASS_NAME;
            var filterType = className.substring(className.lastIndexOf(".") + 1);
            switch (filterType) {
            case "Spatial":
                switch (filter.type) {
                case OpenLayers.Filter.Spatial.BBOX:
                    params.bbox = filter.value.toArray();
                    if (this.srsInBBOX && filter.projection) {
                        params.bbox.push(filter.projection.getCode());
                    }
                    break;
                case OpenLayers.Filter.Spatial.DWITHIN:
                    params.tolerance = filter.distance;
                case OpenLayers.Filter.Spatial.WITHIN:
                    params.lon = filter.value.x;
                    params.lat = filter.value.y;
                    break;
                default:
                    OpenLayers.Console.warn("Unknown spatial filter type " + filter.type);
                }
                break;
            case "Comparison":
                var op = cmpToStr[filter.type];
                if (op !== undefined) {
                    var value = filter.value;
                    if (filter.type == OpenLayers.Filter.Comparison.LIKE) {
                        value = regex2value(value);
                        if (this.wildcarded) {
                            value = "%" + value + "%";
                        }
                    }
                    params[filter.property + "__" + op] = value;
                    params.queryable = params.queryable || [];
                    params.queryable.push(filter.property);
                } else {
                    OpenLayers.Console.warn("Unknown comparison filter type " + filter.type);
                }
                break;
            case "Logical":
                if (filter.type === OpenLayers.Filter.Logical.AND) {
                    for (var i = 0, len = filter.filters.length; i < len; i++) {
                        params = this.write(filter.filters[i], params);
                    }
                } else {
                    OpenLayers.Console.warn("Unsupported logical filter type " + filter.type);
                }
                break;
            default:
                OpenLayers.Console.warn("Unknown filter type " + filterType);
            }
            return params;
        },
        CLASS_NAME: "OpenLayers.Format.QueryStringFilter"
    });
})();
OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(OpenLayers.Format.XML, {
    VERSION: "1.0.0",
    namespaces: {
        sos: "http://www.opengis.net/sos/1.0",
        gml: "http://www.opengis.net/gml",
        sa: "http://www.opengis.net/sampling/1.0",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd",
    defaultPrefix: "sos",
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var info = {
            features: []
        };
        this.readNode(data, info);
        var features = [];
        for (var i = 0, len = info.features.length; i < len; i++) {
            var container = info.features[i];
            if (this.internalProjection && this.externalProjection && container.components[0]) {
                container.components[0].transform(this.externalProjection, this.internalProjection);
            }
            var feature = new OpenLayers.Feature.Vector(container.components[0], container.attributes);
            features.push(feature);
        }
        return features;
    },
    readers: {
        "sa": {
            "SamplingPoint": function (node, obj) {
                if (!obj.attributes) {
                    var feature = {
                        attributes: {}
                    };
                    obj.features.push(feature);
                    obj = feature;
                }
                obj.attributes.id = this.getAttributeNS(node, this.namespaces.gml, "id");
                this.readChildNodes(node, obj);
            },
            "position": function (node, obj) {
                this.readChildNodes(node, obj);
            }
        },
        "gml": OpenLayers.Util.applyDefaults({
            "FeatureCollection": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "featureMember": function (node, obj) {
                var feature = {
                    attributes: {}
                };
                obj.features.push(feature);
                this.readChildNodes(node, feature);
            },
            "name": function (node, obj) {
                obj.attributes.name = this.getChildValue(node);
            },
            "pos": function (node, obj) {
                if (!this.externalProjection) {
                    this.externalProjection = new OpenLayers.Projection(node.getAttribute("srsName"));
                }
                OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(this, [node, obj]);
            }
        }, OpenLayers.Format.GML.v3.prototype.readers.gml)
    },
    writers: {
        "sos": {
            "GetFeatureOfInterest": function (options) {
                var node = this.createElementNSPlus("GetFeatureOfInterest", {
                    attributes: {
                        version: this.VERSION,
                        service: 'SOS',
                        "xsi:schemaLocation": this.schemaLocation
                    }
                });
                for (var i = 0, len = options.fois.length; i < len; i++) {
                    this.writeNode("FeatureOfInterestId", {
                        foi: options.fois[i]
                    }, node);
                }
                return node;
            },
            "FeatureOfInterestId": function (options) {
                var node = this.createElementNSPlus("FeatureOfInterestId", {
                    value: options.foi
                });
                return node;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest"
});
OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ows: "http://www.opengis.net/ows",
        gml: "http://www.opengis.net/gml",
        sos: "http://www.opengis.net/sos/1.0",
        ogc: "http://www.opengis.net/ogc",
        om: "http://www.opengis.net/om/1.0",
        sa: "http://www.opengis.net/sampling/1.0",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    VERSION: "1.0.0",
    schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd",
    defaultPrefix: "sos",
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var info = {
            measurements: [],
            observations: []
        };
        this.readNode(data, info);
        return info;
    },
    write: function (options) {
        var node = this.writeNode("sos:GetObservation", options);
        node.setAttribute("xmlns:om", this.namespaces.om);
        node.setAttribute("xmlns:ogc", this.namespaces.ogc);
        this.setAttributeNS(node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation);
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    readers: {
        "om": {
            "ObservationCollection": function (node, obj) {
                obj.id = this.getAttributeNS(node, this.namespaces.gml, "id");
                this.readChildNodes(node, obj);
            },
            "member": function (node, observationCollection) {
                this.readChildNodes(node, observationCollection);
            },
            "Measurement": function (node, observationCollection) {
                var measurement = {};
                observationCollection.measurements.push(measurement);
                this.readChildNodes(node, measurement);
            },
            "Observation": function (node, observationCollection) {
                var observation = {};
                observationCollection.observations.push(observation);
                this.readChildNodes(node, observation);
            },
            "samplingTime": function (node, measurement) {
                var samplingTime = {};
                measurement.samplingTime = samplingTime;
                this.readChildNodes(node, samplingTime);
            },
            "observedProperty": function (node, measurement) {
                measurement.observedProperty = this.getAttributeNS(node, this.namespaces.xlink, "href");
                this.readChildNodes(node, measurement);
            },
            "procedure": function (node, measurement) {
                measurement.procedure = this.getAttributeNS(node, this.namespaces.xlink, "href");
                this.readChildNodes(node, measurement);
            },
            "featureOfInterest": function (node, observation) {
                var foi = {
                    features: []
                };
                observation.fois = [];
                observation.fois.push(foi);
                this.readChildNodes(node, foi);
                var features = [];
                for (var i = 0, len = foi.features.length; i < len; i++) {
                    var feature = foi.features[i];
                    features.push(new OpenLayers.Feature.Vector(feature.components[0], feature.attributes));
                }
                foi.features = features;
            },
            "result": function (node, measurement) {
                var result = {};
                measurement.result = result;
                if (this.getChildValue(node) !== '') {
                    result.value = this.getChildValue(node);
                    result.uom = node.getAttribute("uom");
                } else {
                    this.readChildNodes(node, result);
                }
            }
        },
        "sa": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa,
        "gml": OpenLayers.Util.applyDefaults({
            "TimeInstant": function (node, samplingTime) {
                var timeInstant = {};
                samplingTime.timeInstant = timeInstant;
                this.readChildNodes(node, timeInstant);
            },
            "timePosition": function (node, timeInstant) {
                timeInstant.timePosition = this.getChildValue(node);
            }
        }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml)
    },
    writers: {
        "sos": {
            "GetObservation": function (options) {
                var node = this.createElementNSPlus("GetObservation", {
                    attributes: {
                        version: this.VERSION,
                        service: 'SOS'
                    }
                });
                this.writeNode("offering", options, node);
                if (options.eventTime) {
                    this.writeNode("eventTime", options, node);
                }
                for (var procedure in options.procedures) {
                    this.writeNode("procedure", options.procedures[procedure], node);
                }
                for (var observedProperty in options.observedProperties) {
                    this.writeNode("observedProperty", options.observedProperties[observedProperty], node);
                }
                if (options.foi) {
                    this.writeNode("featureOfInterest", options.foi, node);
                }
                this.writeNode("responseFormat", options, node);
                if (options.resultModel) {
                    this.writeNode("resultModel", options, node);
                }
                if (options.responseMode) {
                    this.writeNode("responseMode", options, node);
                }
                return node;
            },
            "featureOfInterest": function (foi) {
                var node = this.createElementNSPlus("featureOfInterest");
                this.writeNode("ObjectID", foi.objectId, node);
                return node;
            },
            "ObjectID": function (options) {
                return this.createElementNSPlus("ObjectID", {
                    value: options
                });
            },
            "responseFormat": function (options) {
                return this.createElementNSPlus("responseFormat", {
                    value: options.responseFormat
                });
            },
            "procedure": function (procedure) {
                return this.createElementNSPlus("procedure", {
                    value: procedure
                });
            },
            "offering": function (options) {
                return this.createElementNSPlus("offering", {
                    value: options.offering
                });
            },
            "observedProperty": function (observedProperty) {
                return this.createElementNSPlus("observedProperty", {
                    value: observedProperty
                });
            },
            "eventTime": function (options) {
                var node = this.createElementNSPlus("eventTime");
                if (options.eventTime === 'latest') {
                    this.writeNode("ogc:TM_Equals", options, node);
                }
                return node;
            },
            "resultModel": function (options) {
                return this.createElementNSPlus("resultModel", {
                    value: options.resultModel
                });
            },
            "responseMode": function (options) {
                return this.createElementNSPlus("responseMode", {
                    value: options.responseMode
                });
            }
        },
        "ogc": {
            "TM_Equals": function (options) {
                var node = this.createElementNSPlus("ogc:TM_Equals");
                this.writeNode("ogc:PropertyName", {
                    property: "urn:ogc:data:time:iso8601"
                }, node);
                if (options.eventTime === 'latest') {
                    this.writeNode("gml:TimeInstant", {
                        value: 'latest'
                    }, node);
                }
                return node;
            },
            "PropertyName": function (options) {
                return this.createElementNSPlus("ogc:PropertyName", {
                    value: options.property
                });
            }
        },
        "gml": {
            "TimeInstant": function (options) {
                var node = this.createElementNSPlus("gml:TimeInstant");
                this.writeNode("gml:timePosition", options, node);
                return node;
            },
            "timePosition": function (options) {
                var node = this.createElementNSPlus("gml:timePosition", {
                    value: options.value
                });
                return node;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.SOSGetObservation"
});
OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, {
    autoActivate: true,
    element: null,
    prefix: '',
    separator: ', ',
    suffix: '',
    numDigits: 5,
    granularity: 10,
    emptyString: null,
    lastXy: null,
    displayProjection: null,
    destroy: function () {
        this.deactivate();
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    activate: function () {
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            this.map.events.register('mousemove', this, this.redraw);
            this.map.events.register('mouseout', this, this.reset);
            this.redraw();
            return true;
        } else {
            return false;
        }
    },
    deactivate: function () {
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.map.events.unregister('mousemove', this, this.redraw);
            this.map.events.unregister('mouseout', this, this.reset);
            this.element.innerHTML = "";
            return true;
        } else {
            return false;
        }
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!this.element) {
            this.div.left = "";
            this.div.top = "";
            this.element = this.div;
        }
        return this.div;
    },
    redraw: function (evt) {
        var lonLat;
        if (evt == null) {
            this.reset();
            return;
        } else {
            if (this.lastXy == null || Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) {
                this.lastXy = evt.xy;
                return;
            }
            lonLat = this.map.getLonLatFromPixel(evt.xy);
            if (!lonLat) {
                return;
            }
            if (this.displayProjection) {
                lonLat.transform(this.map.getProjectionObject(), this.displayProjection);
            }
            this.lastXy = evt.xy;
        }
        var newHtml = this.formatOutput(lonLat);
        if (newHtml != this.element.innerHTML) {
            this.element.innerHTML = newHtml;
        }
    },
    reset: function (evt) {
        if (this.emptyString != null) {
            this.element.innerHTML = this.emptyString;
        }
    },
    formatOutput: function (lonLat) {
        var digits = parseInt(this.numDigits);
        var newHtml = this.prefix + lonLat.lon.toFixed(digits) + this.separator + lonLat.lat.toFixed(digits) + this.suffix;
        return newHtml;
    },
    CLASS_NAME: "OpenLayers.Control.MousePosition"
});
OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["locationupdated", "locationfailed", "locationuncapable"],
    geolocation: navigator.geolocation,
    bind: true,
    watch: false,
    geolocationOptions: null,
    initialize: function (options) {
        this.EVENT_TYPES = OpenLayers.Control.Geolocate.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        this.geolocationOptions = {};
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
    },
    destroy: function () {
        this.deactivate();
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    activate: function () {
        if (!this.geolocation) {
            this.events.triggerEvent("locationuncapable");
            return false;
        }
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            if (this.watch) {
                this.watchId = this.geolocation.watchPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions);
            } else {
                this.getCurrentLocation();
            }
            return true;
        }
        return false;
    },
    deactivate: function () {
        if (this.active && this.watchId !== null) {
            this.geolocation.clearWatch(this.watchId);
        }
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    geolocate: function (position) {
        var center = new OpenLayers.LonLat(position.coords.longitude, position.coords.latitude).transform(new OpenLayers.Projection("EPSG:4326"), this.map.getProjectionObject());
        if (this.bind) {
            this.map.setCenter(center);
        }
        this.events.triggerEvent("locationupdated", {
            position: position,
            point: new OpenLayers.Geometry.Point(center.lon, center.lat)
        });
    },
    getCurrentLocation: function () {
        if (!this.active || this.watch) {
            return false;
        }
        this.geolocation.getCurrentPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions);
        return true;
    },
    failure: function (error) {
        this.events.triggerEvent("locationfailed", {
            error: error
        });
    },
    CLASS_NAME: "OpenLayers.Control.Geolocate"
});
OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_TOGGLE,
    previous: null,
    previousOptions: null,
    next: null,
    nextOptions: null,
    limit: 50,
    autoActivate: true,
    clearOnDeactivate: false,
    registry: null,
    nextStack: null,
    previousStack: null,
    listeners: null,
    restoring: false,
    initialize: function (options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.registry = OpenLayers.Util.extend({
            "moveend": this.getState
        }, this.registry);
        var previousOptions = {
            trigger: OpenLayers.Function.bind(this.previousTrigger, this),
            displayClass: this.displayClass + " " + this.displayClass + "Previous"
        };
        OpenLayers.Util.extend(previousOptions, this.previousOptions);
        this.previous = new OpenLayers.Control.Button(previousOptions);
        var nextOptions = {
            trigger: OpenLayers.Function.bind(this.nextTrigger, this),
            displayClass: this.displayClass + " " + this.displayClass + "Next"
        };
        OpenLayers.Util.extend(nextOptions, this.nextOptions);
        this.next = new OpenLayers.Control.Button(nextOptions);
        this.clear();
    },
    onPreviousChange: function (state, length) {
        if (state && !this.previous.active) {
            this.previous.activate();
        } else if (!state && this.previous.active) {
            this.previous.deactivate();
        }
    },
    onNextChange: function (state, length) {
        if (state && !this.next.active) {
            this.next.activate();
        } else if (!state && this.next.active) {
            this.next.deactivate();
        }
    },
    destroy: function () {
        OpenLayers.Control.prototype.destroy.apply(this);
        this.previous.destroy();
        this.next.destroy();
        this.deactivate();
        for (var prop in this) {
            this[prop] = null;
        }
    },
    setMap: function (map) {
        this.map = map;
        this.next.setMap(map);
        this.previous.setMap(map);
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        this.next.draw();
        this.previous.draw();
    },
    previousTrigger: function () {
        var current = this.previousStack.shift();
        var state = this.previousStack.shift();
        if (state != undefined) {
            this.nextStack.unshift(current);
            this.previousStack.unshift(state);
            this.restoring = true;
            this.restore(state);
            this.restoring = false;
            this.onNextChange(this.nextStack[0], this.nextStack.length);
            this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1);
        } else {
            this.previousStack.unshift(current);
        }
        return state;
    },
    nextTrigger: function () {
        var state = this.nextStack.shift();
        if (state != undefined) {
            this.previousStack.unshift(state);
            this.restoring = true;
            this.restore(state);
            this.restoring = false;
            this.onNextChange(this.nextStack[0], this.nextStack.length);
            this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1);
        }
        return state;
    },
    clear: function () {
        this.previousStack = [];
        this.previous.deactivate();
        this.nextStack = [];
        this.next.deactivate();
    },
    getState: function () {
        return {
            center: this.map.getCenter(),
            resolution: this.map.getResolution(),
            projection: this.map.getProjectionObject(),
            units: this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units
        };
    },
    restore: function (state) {
        var center, zoom;
        if (this.map.getProjectionObject() == state.projection) {
            zoom = this.map.getZoomForResolution(state.resolution);
            center = state.center;
        } else {
            center = state.center.clone();
            center.transform(state.projection, this.map.getProjectionObject());
            var sourceUnits = state.units;
            var targetUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units;
            var resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1;
            zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution);
        }
        this.map.setCenter(center, zoom);
    },
    setListeners: function () {
        this.listeners = {};
        for (var type in this.registry) {
            this.listeners[type] = OpenLayers.Function.bind(function () {
                if (!this.restoring) {
                    var state = this.registry[type].apply(this, arguments);
                    this.previousStack.unshift(state);
                    if (this.previousStack.length > 1) {
                        this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1);
                    }
                    if (this.previousStack.length > (this.limit + 1)) {
                        this.previousStack.pop();
                    }
                    if (this.nextStack.length > 0) {
                        this.nextStack = [];
                        this.onNextChange(null, 0);
                    }
                }
                return true;
            }, this);
        }
    },
    activate: function () {
        var activated = false;
        if (this.map) {
            if (OpenLayers.Control.prototype.activate.apply(this)) {
                if (this.listeners == null) {
                    this.setListeners();
                }
                for (var type in this.listeners) {
                    this.map.events.register(type, this, this.listeners[type]);
                }
                activated = true;
                if (this.previousStack.length == 0) {
                    this.initStack();
                }
            }
        }
        return activated;
    },
    initStack: function () {
        if (this.map.getCenter()) {
            this.listeners.moveend();
        }
    },
    deactivate: function () {
        var deactivated = false;
        if (this.map) {
            if (OpenLayers.Control.prototype.deactivate.apply(this)) {
                for (var type in this.listeners) {
                    this.map.events.unregister(type, this, this.listeners[type]);
                }
                if (this.clearOnDeactivate) {
                    this.clear();
                }
                deactivated = true;
            }
        }
        return deactivated;
    },
    CLASS_NAME: "OpenLayers.Control.NavigationHistory"
});
OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, {
    url: null,
    headers: null,
    params: null,
    callback: null,
    scope: null,
    readWithPOST: false,
    wildcarded: false,
    srsInBBOX: false,
    initialize: function (options) {
        options = options || {};
        this.params = {};
        this.headers = {};
        OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
        if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
            var format = new OpenLayers.Format.QueryStringFilter({
                wildcarded: this.wildcarded,
                srsInBBOX: this.srsInBBOX
            });
            this.filterToParams = function (filter, params) {
                return format.write(filter, params);
            }
        }
    },
    destroy: function () {
        this.params = null;
        this.headers = null;
        OpenLayers.Protocol.prototype.destroy.apply(this);
    },
    read: function (options) {
        OpenLayers.Protocol.prototype.read.apply(this, arguments);
        options = options || {};
        options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params);
        options = OpenLayers.Util.applyDefaults(options, this.options);
        if (options.filter && this.filterToParams) {
            options.params = this.filterToParams(options.filter, options.params);
        }
        var readWithPOST = (options.readWithPOST !== undefined) ? options.readWithPOST : this.readWithPOST;
        var resp = new OpenLayers.Protocol.Response({
            requestType: "read"
        });
        if (readWithPOST) {
            resp.priv = OpenLayers.Request.POST({
                url: options.url,
                callback: this.createCallback(this.handleRead, resp, options),
                data: OpenLayers.Util.getParameterString(options.params),
                headers: {
                    "Content-Type": "application/x-www-form-urlencoded"
                }
            });
        } else {
            resp.priv = OpenLayers.Request.GET({
                url: options.url,
                callback: this.createCallback(this.handleRead, resp, options),
                params: options.params,
                headers: options.headers
            });
        }
        return resp;
    },
    handleRead: function (resp, options) {
        this.handleResponse(resp, options);
    },
    create: function (features, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = new OpenLayers.Protocol.Response({
            reqFeatures: features,
            requestType: "create"
        });
        resp.priv = OpenLayers.Request.POST({
            url: options.url,
            callback: this.createCallback(this.handleCreate, resp, options),
            headers: options.headers,
            data: this.format.write(features)
        });
        return resp;
    },
    handleCreate: function (resp, options) {
        this.handleResponse(resp, options);
    },
    update: function (feature, options) {
        options = options || {};
        var url = options.url || feature.url || this.options.url + "/" + feature.fid;
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = new OpenLayers.Protocol.Response({
            reqFeatures: feature,
            requestType: "update"
        });
        resp.priv = OpenLayers.Request.PUT({
            url: url,
            callback: this.createCallback(this.handleUpdate, resp, options),
            headers: options.headers,
            data: this.format.write(feature)
        });
        return resp;
    },
    handleUpdate: function (resp, options) {
        this.handleResponse(resp, options);
    },
    "delete": function (feature, options) {
        options = options || {};
        var url = options.url || feature.url || this.options.url + "/" + feature.fid;
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = new OpenLayers.Protocol.Response({
            reqFeatures: feature,
            requestType: "delete"
        });
        resp.priv = OpenLayers.Request.DELETE({
            url: url,
            callback: this.createCallback(this.handleDelete, resp, options),
            headers: options.headers
        });
        return resp;
    },
    handleDelete: function (resp, options) {
        this.handleResponse(resp, options);
    },
    handleResponse: function (resp, options) {
        var request = resp.priv;
        if (options.callback) {
            if (request.status >= 200 && request.status < 300) {
                if (resp.requestType != "delete") {
                    resp.features = this.parseFeatures(request);
                }
                resp.code = OpenLayers.Protocol.Response.SUCCESS;
            } else {
                resp.code = OpenLayers.Protocol.Response.FAILURE;
            }
            options.callback.call(options.scope, resp);
        }
    },
    parseFeatures: function (request) {
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        if (!doc || doc.length <= 0) {
            return null;
        }
        return this.format.read(doc);
    },
    commit: function (features, options) {
        options = OpenLayers.Util.applyDefaults(options, this.options);
        var resp = [],
            nResponses = 0;
        var types = {};
        types[OpenLayers.State.INSERT] = [];
        types[OpenLayers.State.UPDATE] = [];
        types[OpenLayers.State.DELETE] = [];
        var feature, list, requestFeatures = [];
        for (var i = 0, len = features.length; i < len; ++i) {
            feature = features[i];
            list = types[feature.state];
            if (list) {
                list.push(feature);
                requestFeatures.push(feature);
            }
        }
        var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length;
        var success = true;
        var finalResponse = new OpenLayers.Protocol.Response({
            reqFeatures: requestFeatures
        });

        function insertCallback(response) {
            var len = response.features ? response.features.length : 0;
            var fids = new Array(len);
            for (var i = 0; i < len; ++i) {
                fids[i] = response.features[i].fid;
            }
            finalResponse.insertIds = fids;
            callback.apply(this, [response]);
        }

        function callback(response) {
            this.callUserCallback(response, options);
            success = success && response.success();
            nResponses++;
            if (nResponses >= nRequests) {
                if (options.callback) {
                    finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE;
                    options.callback.apply(options.scope, [finalResponse]);
                }
            }
        }
        var queue = types[OpenLayers.State.INSERT];
        if (queue.length > 0) {
            resp.push(this.create(queue, OpenLayers.Util.applyDefaults({
                callback: insertCallback,
                scope: this
            }, options.create)));
        }
        queue = types[OpenLayers.State.UPDATE];
        for (var i = queue.length - 1; i >= 0; --i) {
            resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({
                callback: callback,
                scope: this
            }, options.update)));
        }
        queue = types[OpenLayers.State.DELETE];
        for (var i = queue.length - 1; i >= 0; --i) {
            resp.push(this["delete"](queue[i], OpenLayers.Util.applyDefaults({
                callback: callback,
                scope: this
            }, options["delete"])));
        }
        return resp;
    },
    abort: function (response) {
        if (response) {
            response.priv.abort();
        }
    },
    callUserCallback: function (resp, options) {
        var opt = options[resp.requestType];
        if (opt && opt.callback) {
            opt.callback.call(opt.scope, resp);
        }
    },
    CLASS_NAME: "OpenLayers.Protocol.HTTP"
});
OpenLayers.Format.CSWGetDomain = function (options) {
    options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetDomain.DEFAULTS);
    var cls = OpenLayers.Format.CSWGetDomain["v" + options.version.replace(/\./g, "_")];
    if (!cls) {
        throw "Unsupported CSWGetDomain version: " + options.version;
    }
    return new cls(options);
};
OpenLayers.Format.CSWGetDomain.DEFAULTS = {
    "version": "2.0.2"
};
OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
        csw: "http://www.opengis.net/cat/csw/2.0.2"
    },
    defaultPrefix: "csw",
    version: "2.0.2",
    schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd",
    PropertyName: null,
    ParameterName: null,
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var obj = {};
        this.readNode(data, obj);
        return obj;
    },
    readers: {
        "csw": {
            "GetDomainResponse": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "DomainValues": function (node, obj) {
                if (!(OpenLayers.Util.isArray(obj.DomainValues))) {
                    obj.DomainValues = [];
                }
                var attrs = node.attributes;
                var domainValue = {};
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    domainValue[attrs[i].name] = attrs[i].nodeValue;
                }
                this.readChildNodes(node, domainValue);
                obj.DomainValues.push(domainValue);
            },
            "PropertyName": function (node, obj) {
                obj.PropertyName = this.getChildValue(node);
            },
            "ParameterName": function (node, obj) {
                obj.ParameterName = this.getChildValue(node);
            },
            "ListOfValues": function (node, obj) {
                if (!(OpenLayers.Util.isArray(obj.ListOfValues))) {
                    obj.ListOfValues = [];
                }
                this.readChildNodes(node, obj.ListOfValues);
            },
            "Value": function (node, obj) {
                var attrs = node.attributes;
                var value = {};
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    value[attrs[i].name] = attrs[i].nodeValue;
                }
                value.value = this.getChildValue(node);
                obj.push({
                    Value: value
                });
            },
            "ConceptualScheme": function (node, obj) {
                obj.ConceptualScheme = {};
                this.readChildNodes(node, obj.ConceptualScheme);
            },
            "Name": function (node, obj) {
                obj.Name = this.getChildValue(node);
            },
            "Document": function (node, obj) {
                obj.Document = this.getChildValue(node);
            },
            "Authority": function (node, obj) {
                obj.Authority = this.getChildValue(node);
            },
            "RangeOfValues": function (node, obj) {
                obj.RangeOfValues = {};
                this.readChildNodes(node, obj.RangeOfValues);
            },
            "MinValue": function (node, obj) {
                var attrs = node.attributes;
                var value = {};
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    value[attrs[i].name] = attrs[i].nodeValue;
                }
                value.value = this.getChildValue(node);
                obj.MinValue = value;
            },
            "MaxValue": function (node, obj) {
                var attrs = node.attributes;
                var value = {};
                for (var i = 0, len = attrs.length; i < len; ++i) {
                    value[attrs[i].name] = attrs[i].nodeValue;
                }
                value.value = this.getChildValue(node);
                obj.MaxValue = value;
            }
        }
    },
    write: function (options) {
        var node = this.writeNode("csw:GetDomain", options);
        return OpenLayers.Format.XML.prototype.write.apply(this, [node]);
    },
    writers: {
        "csw": {
            "GetDomain": function (options) {
                var node = this.createElementNSPlus("csw:GetDomain", {
                    attributes: {
                        service: "CSW",
                        version: this.version
                    }
                });
                if (options.PropertyName || this.PropertyName) {
                    this.writeNode("csw:PropertyName", options.PropertyName || this.PropertyName, node);
                } else if (options.ParameterName || this.ParameterName) {
                    this.writeNode("csw:ParameterName", options.ParameterName || this.ParameterName, node);
                }
                this.readChildNodes(node, options);
                return node;
            },
            "PropertyName": function (value) {
                var node = this.createElementNSPlus("csw:PropertyName", {
                    value: value
                });
                return node;
            },
            "ParameterName": function (value) {
                var node = this.createElementNSPlus("csw:ParameterName", {
                    value: value
                });
                return node;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.CSWGetDomain.v2_0_2"
});
OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, {
    distance: 20,
    threshold: null,
    features: null,
    clusters: null,
    clustering: false,
    resolution: null,
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.call(this);
        if (activated) {
            this.layer.events.on({
                "beforefeaturesadded": this.cacheFeatures,
                "moveend": this.cluster,
                scope: this
            });
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this);
        if (deactivated) {
            this.clearCache();
            this.layer.events.un({
                "beforefeaturesadded": this.cacheFeatures,
                "moveend": this.cluster,
                scope: this
            });
        }
        return deactivated;
    },
    cacheFeatures: function (event) {
        var propagate = true;
        if (!this.clustering) {
            this.clearCache();
            this.features = event.features;
            this.cluster();
            propagate = false;
        }
        return propagate;
    },
    clearCache: function () {
        this.features = null;
    },
    cluster: function (event) {
        if ((!event || event.zoomChanged) && this.features) {
            var resolution = this.layer.map.getResolution();
            if (resolution != this.resolution || !this.clustersExist()) {
                this.resolution = resolution;
                var clusters = [];
                var feature, clustered, cluster;
                for (var i = 0; i < this.features.length; ++i) {
                    feature = this.features[i];
                    if (feature.geometry) {
                        clustered = false;
                        for (var j = clusters.length - 1; j >= 0; --j) {
                            cluster = clusters[j];
                            if (this.shouldCluster(cluster, feature)) {
                                this.addToCluster(cluster, feature);
                                clustered = true;
                                break;
                            }
                        }
                        if (!clustered) {
                            clusters.push(this.createCluster(this.features[i]));
                        }
                    }
                }
                this.layer.removeAllFeatures();
                if (clusters.length > 0) {
                    if (this.threshold > 1) {
                        var clone = clusters.slice();
                        clusters = [];
                        var candidate;
                        for (var i = 0, len = clone.length; i < len; ++i) {
                            candidate = clone[i];
                            if (candidate.attributes.count < this.threshold) {
                                Array.prototype.push.apply(clusters, candidate.cluster);
                            } else {
                                clusters.push(candidate);
                            }
                        }
                    }
                    this.clustering = true;
                    this.layer.addFeatures(clusters);
                    this.clustering = false;
                }
                this.clusters = clusters;
            }
        }
    },
    clustersExist: function () {
        var exist = false;
        if (this.clusters && this.clusters.length > 0 && this.clusters.length == this.layer.features.length) {
            exist = true;
            for (var i = 0; i < this.clusters.length; ++i) {
                if (this.clusters[i] != this.layer.features[i]) {
                    exist = false;
                    break;
                }
            }
        }
        return exist;
    },
    shouldCluster: function (cluster, feature) {
        var cc = cluster.geometry.getBounds().getCenterLonLat();
        var fc = feature.geometry.getBounds().getCenterLonLat();
        var distance = (Math.sqrt(Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2)) / this.resolution);
        return (distance <= this.distance);
    },
    addToCluster: function (cluster, feature) {
        cluster.cluster.push(feature);
        cluster.attributes.count += 1;
    },
    createCluster: function (feature) {
        var center = feature.geometry.getBounds().getCenterLonLat();
        var cluster = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(center.lon, center.lat), {
            count: 1
        });
        cluster.cluster = [feature];
        return cluster;
    },
    CLASS_NAME: "OpenLayers.Strategy.Cluster"
});
OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, {
    filter: null,
    cache: null,
    caching: false,
    activate: function () {
        var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments);
        if (activated) {
            this.cache = [];
            this.layer.events.on({
                "beforefeaturesadded": this.handleAdd,
                "beforefeaturesremoved": this.handleRemove,
                scope: this
            });
        }
        return activated;
    },
    deactivate: function () {
        this.cache = null;
        if (this.layer && this.layer.events) {
            this.layer.events.un({
                "beforefeaturesadded": this.handleAdd,
                "beforefeaturesremoved": this.handleRemove,
                scope: this
            });
        }
        return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments);
    },
    handleAdd: function (event) {
        if (!this.caching && this.filter) {
            var features = event.features;
            event.features = [];
            var feature;
            for (var i = 0, ii = features.length; i < ii; ++i) {
                feature = features[i];
                if (this.filter.evaluate(feature)) {
                    event.features.push(feature);
                } else {
                    this.cache.push(feature);
                }
            }
        }
    },
    handleRemove: function (event) {
        if (!this.caching) {
            this.cache = [];
        }
    },
    setFilter: function (filter) {
        this.filter = filter;
        var previousCache = this.cache;
        this.cache = [];
        this.handleAdd({
            features: this.layer.features
        });
        if (this.cache.length > 0) {
            this.caching = true;
            this.layer.removeFeatures(this.cache.slice());
            this.caching = false;
        }
        if (previousCache.length > 0) {
            var event = {
                features: previousCache
            };
            this.handleAdd(event);
            if (event.features.length > 0) {
                this.caching = true;
                this.layer.addFeatures(event.features);
                this.caching = false;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Strategy.Filter"
});
OpenLayers.Protocol.SOS = function (options) {
    options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.SOS.DEFAULTS);
    var cls = OpenLayers.Protocol.SOS["v" + options.version.replace(/\./g, "_")];
    if (!cls) {
        throw "Unsupported SOS version: " + options.version;
    }
    return new cls(options);
};
OpenLayers.Protocol.SOS.DEFAULTS = {
    "version": "1.0.0"
};
OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, {
    rssns: "http://backend.userland.com/rss2",
    featureNS: "http://mapserver.gis.umn.edu/mapserver",
    georssns: "http://www.georss.org/georss",
    geons: "http://www.w3.org/2003/01/geo/wgs84_pos#",
    featureTitle: "Untitled",
    featureDescription: "No Description",
    gmlParser: null,
    xy: false,
    createGeometryFromItem: function (item) {
        var point = this.getElementsByTagNameNS(item, this.georssns, "point");
        var lat = this.getElementsByTagNameNS(item, this.geons, 'lat');
        var lon = this.getElementsByTagNameNS(item, this.geons, 'long');
        var line = this.getElementsByTagNameNS(item, this.georssns, "line");
        var polygon = this.getElementsByTagNameNS(item, this.georssns, "polygon");
        var where = this.getElementsByTagNameNS(item, this.georssns, "where");
        var box = this.getElementsByTagNameNS(item, this.georssns, "box");
        if (point.length > 0 || (lat.length > 0 && lon.length > 0)) {
            var location;
            if (point.length > 0) {
                location = OpenLayers.String.trim(point[0].firstChild.nodeValue).split(/\s+/);
                if (location.length != 2) {
                    location = OpenLayers.String.trim(point[0].firstChild.nodeValue).split(/\s*,\s*/);
                }
            } else {
                location = [parseFloat(lat[0].firstChild.nodeValue), parseFloat(lon[0].firstChild.nodeValue)];
            }
            var geometry = new OpenLayers.Geometry.Point(parseFloat(location[1]), parseFloat(location[0]));
        } else if (line.length > 0) {
            var coords = OpenLayers.String.trim(this.concatChildValues(line[0])).split(/\s+/);
            var components = [];
            var point;
            for (var i = 0, len = coords.length; i < len; i += 2) {
                point = new OpenLayers.Geometry.Point(parseFloat(coords[i + 1]), parseFloat(coords[i]));
                components.push(point);
            }
            geometry = new OpenLayers.Geometry.LineString(components);
        } else if (polygon.length > 0) {
            var coords = OpenLayers.String.trim(this.concatChildValues(polygon[0])).split(/\s+/);
            var components = [];
            var point;
            for (var i = 0, len = coords.length; i < len; i += 2) {
                point = new OpenLayers.Geometry.Point(parseFloat(coords[i + 1]), parseFloat(coords[i]));
                components.push(point);
            }
            geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
        } else if (where.length > 0) {
            if (!this.gmlParser) {
                this.gmlParser = new OpenLayers.Format.GML({
                    'xy': this.xy
                });
            }
            var feature = this.gmlParser.parseFeature(where[0]);
            geometry = feature.geometry;
        } else if (box.length > 0) {
            var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/);
            var components = [];
            var point;
            if (coords.length > 3) {
                point = new OpenLayers.Geometry.Point(parseFloat(coords[1]), parseFloat(coords[0]));
                components.push(point);
                point = new OpenLayers.Geometry.Point(parseFloat(coords[1]), parseFloat(coords[2]));
                components.push(point);
                point = new OpenLayers.Geometry.Point(parseFloat(coords[3]), parseFloat(coords[2]));
                components.push(point);
                point = new OpenLayers.Geometry.Point(parseFloat(coords[3]), parseFloat(coords[0]));
                components.push(point);
                point = new OpenLayers.Geometry.Point(parseFloat(coords[1]), parseFloat(coords[0]));
                components.push(point);
            }
            geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]);
        }
        if (geometry && this.internalProjection && this.externalProjection) {
            geometry.transform(this.externalProjection, this.internalProjection);
        }
        return geometry;
    },
    createFeatureFromItem: function (item) {
        var geometry = this.createGeometryFromItem(item);
        var title = this.getChildValue(item, "*", "title", this.featureTitle);
        var description = this.getChildValue(item, "*", "description", this.getChildValue(item, "*", "content", this.getChildValue(item, "*", "summary", this.featureDescription)));
        var link = this.getChildValue(item, "*", "link");
        if (!link) {
            try {
                link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href");
            } catch (e) {
                link = null;
            }
        }
        var id = this.getChildValue(item, "*", "id", null);
        var data = {
            "title": title,
            "description": description,
            "link": link
        };
        var feature = new OpenLayers.Feature.Vector(geometry, data);
        feature.fid = id;
        return feature;
    },
    getChildValue: function (node, nsuri, name, def) {
        var value;
        var eles = this.getElementsByTagNameNS(node, nsuri, name);
        if (eles && eles[0] && eles[0].firstChild && eles[0].firstChild.nodeValue) {
            value = OpenLayers.Format.XML.prototype.getChildValue(eles[0]);
        } else {
            value = (def == undefined) ? "" : def;
        }
        return value;
    },
    read: function (doc) {
        if (typeof doc == "string") {
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
        }
        var itemlist = null;
        itemlist = this.getElementsByTagNameNS(doc, '*', 'item');
        if (itemlist.length == 0) {
            itemlist = this.getElementsByTagNameNS(doc, '*', 'entry');
        }
        var numItems = itemlist.length;
        var features = new Array(numItems);
        for (var i = 0; i < numItems; i++) {
            features[i] = this.createFeatureFromItem(itemlist[i]);
        }
        return features;
    },
    write: function (features) {
        var georss;
        if (OpenLayers.Util.isArray(features)) {
            georss = this.createElementNS(this.rssns, "rss");
            for (var i = 0, len = features.length; i < len; i++) {
                georss.appendChild(this.createFeatureXML(features[i]));
            }
        } else {
            georss = this.createFeatureXML(features);
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [georss]);
    },
    createFeatureXML: function (feature) {
        var geometryNode = this.buildGeometryNode(feature.geometry);
        var featureNode = this.createElementNS(this.rssns, "item");
        var titleNode = this.createElementNS(this.rssns, "title");
        titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : ""));
        var descNode = this.createElementNS(this.rssns, "description");
        descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : ""));
        featureNode.appendChild(titleNode);
        featureNode.appendChild(descNode);
        if (feature.attributes.link) {
            var linkNode = this.createElementNS(this.rssns, "link");
            linkNode.appendChild(this.createTextNode(feature.attributes.link));
            featureNode.appendChild(linkNode);
        }
        for (var attr in feature.attributes) {
            if (attr == "link" || attr == "title" || attr == "description") {
                continue;
            }
            var attrText = this.createTextNode(feature.attributes[attr]);
            var nodename = attr;
            if (attr.search(":") != -1) {
                nodename = attr.split(":")[1];
            }
            var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename);
            attrContainer.appendChild(attrText);
            featureNode.appendChild(attrContainer);
        }
        featureNode.appendChild(geometryNode);
        return featureNode;
    },
    buildGeometryNode: function (geometry) {
        if (this.internalProjection && this.externalProjection) {
            geometry = geometry.clone();
            geometry.transform(this.internalProjection, this.externalProjection);
        }
        var node;
        if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
            node = this.createElementNS(this.georssns, 'georss:polygon');
            node.appendChild(this.buildCoordinatesNode(geometry.components[0]));
        } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") {
            node = this.createElementNS(this.georssns, 'georss:line');
            node.appendChild(this.buildCoordinatesNode(geometry));
        } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            node = this.createElementNS(this.georssns, 'georss:point');
            node.appendChild(this.buildCoordinatesNode(geometry));
        } else {
            throw "Couldn't parse " + geometry.CLASS_NAME;
        }
        return node;
    },
    buildCoordinatesNode: function (geometry) {
        var points = null;
        if (geometry.components) {
            points = geometry.components;
        }
        var path;
        if (points) {
            var numPoints = points.length;
            var parts = new Array(numPoints);
            for (var i = 0; i < numPoints; i++) {
                parts[i] = points[i].y + " " + points[i].x;
            }
            path = parts.join(" ");
        } else {
            path = geometry.y + " " + geometry.x;
        }
        return this.createTextNode(path);
    },
    CLASS_NAME: "OpenLayers.Format.GeoRSS"
});
OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.0.0",
    CLASS_NAME: "OpenLayers.Format.WPSCapabilities"
});
OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        wps: "http://www.opengis.net/wps/1.0.0",
        xlink: "http://www.w3.org/1999/xlink"
    },
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var capabilities = {};
        this.readNode(data, capabilities);
        return capabilities;
    },
    readers: {
        "wps": {
            "Capabilities": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "ProcessOfferings": function (node, obj) {
                obj.processOfferings = {};
                this.readChildNodes(node, obj.processOfferings);
            },
            "Process": function (node, processOfferings) {
                var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion");
                var process = {
                    processVersion: processVersion
                };
                this.readChildNodes(node, process);
                processOfferings[process.identifier] = process;
            },
            "Languages": function (node, obj) {
                obj.languages = [];
                this.readChildNodes(node, obj.languages);
            },
            "Default": function (node, languages) {
                var language = {
                    isDefault: true
                };
                this.readChildNodes(node, language);
                languages.push(language);
            },
            "Supported": function (node, languages) {
                var language = {};
                this.readChildNodes(node, language);
                languages.push(language);
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
    },
    CLASS_NAME: "OpenLayers.Format.WPSCapabilities.v1_0_0"
});
OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_TOOL,
    containerOrigin: null,
    pinchOrigin: null,
    currentCenter: null,
    autoActivate: true,
    initialize: function (options) {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.handler = new OpenLayers.Handler.Pinch(this, {
            start: this.pinchStart,
            move: this.pinchMove,
            done: this.pinchDone
        }, this.handlerOptions);
    },
    activate: function () {
        var activated = OpenLayers.Control.prototype.activate.apply(this, arguments);
        if (activated) {
            this.map.events.on({
                moveend: this.updateContainerOrigin,
                scope: this
            });
            this.updateContainerOrigin();
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Control.prototype.deactivate.apply(this, arguments);
        if (this.map && this.map.events) {
            this.map.events.un({
                moveend: this.updateContainerOrigin,
                scope: this
            });
        }
        return deactivated;
    },
    updateContainerOrigin: function () {
        var container = this.map.layerContainerDiv;
        this.containerOrigin = {
            x: parseInt(container.style.left, 10),
            y: parseInt(container.style.top, 10)
        };
    },
    pinchStart: function (evt, pinchData) {
        this.pinchOrigin = evt.xy;
        this.currentCenter = evt.xy;
    },
    pinchMove: function (evt, pinchData) {
        var scale = pinchData.scale;
        var containerOrigin = this.containerOrigin;
        var pinchOrigin = this.pinchOrigin;
        var current = evt.xy;
        var dx = Math.round((current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x));
        var dy = Math.round((current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y));
        this.applyTransform("translate(" + dx + "px, " + dy + "px) scale(" + scale + ")");
        this.currentCenter = current;
    },
    applyTransform: function (transform) {
        var style = this.map.layerContainerDiv.style;
        style['-webkit-transform'] = transform;
        style['-moz-transform'] = transform;
    },
    pinchDone: function (evt, start, last) {
        this.applyTransform("");
        var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
        if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
            var resolution = this.map.getResolutionForZoom(zoom);
            var location = this.map.getLonLatFromPixel(this.pinchOrigin);
            var zoomPixel = this.currentCenter;
            var size = this.map.getSize();
            location.lon += resolution * ((size.w / 2) - zoomPixel.x);
            location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
            this.map.setCenter(location, zoom);
        }
    },
    CLASS_NAME: "OpenLayers.Control.PinchZoom"
});
OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, {
    dragPan: null,
    dragPanOptions: null,
    pinchZoom: null,
    pinchZoomOptions: null,
    clickHandlerOptions: null,
    documentDrag: false,
    autoActivate: true,
    initialize: function (options) {
        this.handlers = {};
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
    },
    destroy: function () {
        this.deactivate();
        if (this.dragPan) {
            this.dragPan.destroy();
        }
        this.dragPan = null;
        if (this.pinchZoom) {
            this.pinchZoom.destroy();
            delete this.pinchZoom;
        }
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    activate: function () {
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            this.dragPan.activate();
            this.handlers.click.activate();
            this.pinchZoom.activate();
            return true;
        }
        return false;
    },
    deactivate: function () {
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.dragPan.deactivate();
            this.handlers.click.deactivate();
            this.pinchZoom.deactivate();
            return true;
        }
        return false;
    },
    draw: function () {
        var clickCallbacks = {
            click: this.defaultClick,
            dblclick: this.defaultDblClick
        };
        var clickOptions = OpenLayers.Util.extend({
            "double": true,
            stopDouble: true,
            pixelTolerance: 2
        }, this.clickHandlerOptions);
        this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions);
        this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({
            map: this.map,
            documentDrag: this.documentDrag
        }, this.dragPanOptions));
        this.dragPan.draw();
        this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({
            map: this.map
        }, this.pinchZoomOptions));
    },
    defaultClick: function (evt) {
        if (evt.lastTouches && evt.lastTouches.length == 2) {
            this.map.zoomOut();
        }
    },
    defaultDblClick: function (evt) {
        var newCenter = this.map.getLonLatFromViewPortPx(evt.xy);
        this.map.setCenter(newCenter, this.map.zoom + 1);
    },
    CLASS_NAME: "OpenLayers.Control.TouchNavigation"
});
OpenLayers.Style2 = OpenLayers.Class({
    id: null,
    name: null,
    title: null,
    description: null,
    layerName: null,
    isDefault: false,
    rules: null,
    initialize: function (config) {
        OpenLayers.Util.extend(this, config);
        this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_");
    },
    destroy: function () {
        for (var i = 0, len = this.rules.length; i < len; i++) {
            this.rules[i].destroy();
        }
        delete this.rules;
    },
    clone: function () {
        var config = OpenLayers.Util.extend({}, this);
        if (this.rules) {
            config.rules = [];
            for (var i = 0, len = this.rules.length; i < len; ++i) {
                config.rules.push(this.rules[i].clone());
            }
        }
        return new OpenLayers.Style2(config);
    },
    CLASS_NAME: "OpenLayers.Style2"
});
OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, {
    initialize: function (name, options) {
        OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments);
    },
    drawMarker: function (marker) {
        var bounds = marker.bounds;
        var topleft = this.map.getLayerPxFromLonLat(new OpenLayers.LonLat(bounds.left, bounds.top));
        var botright = this.map.getLayerPxFromLonLat(new OpenLayers.LonLat(bounds.right, bounds.bottom));
        if (botright == null || topleft == null) {
            marker.display(false);
        } else {
            var sz = new OpenLayers.Size(Math.max(1, botright.x - topleft.x), Math.max(1, botright.y - topleft.y));
            var markerDiv = marker.draw(topleft, sz);
            if (!marker.drawn) {
                this.div.appendChild(markerDiv);
                marker.drawn = true;
            }
        }
    },
    removeMarker: function (marker) {
        OpenLayers.Util.removeItem(this.markers, marker);
        if ((marker.div != null) && (marker.div.parentNode == this.div)) {
            this.div.removeChild(marker.div);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.Boxes"
});
OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.WFSCapabilities.v1, {
    initialize: function (options) {
        OpenLayers.Format.WFSCapabilities.v1.prototype.initialize.apply(this, [options]);
    },
    read_cap_Service: function (capabilities, node) {
        var service = {};
        this.runChildNodes(service, node);
        capabilities.service = service;
    },
    read_cap_Fees: function (service, node) {
        var fees = this.getChildValue(node);
        if (fees && fees.toLowerCase() != "none") {
            service.fees = fees;
        }
    },
    read_cap_AccessConstraints: function (service, node) {
        var constraints = this.getChildValue(node);
        if (constraints && constraints.toLowerCase() != "none") {
            service.accessConstraints = constraints;
        }
    },
    read_cap_OnlineResource: function (service, node) {
        var onlineResource = this.getChildValue(node);
        if (onlineResource && onlineResource.toLowerCase() != "none") {
            service.onlineResource = onlineResource;
        }
    },
    read_cap_Keywords: function (service, node) {
        var keywords = this.getChildValue(node);
        if (keywords && keywords.toLowerCase() != "none") {
            service.keywords = keywords.split(', ');
        }
    },
    read_cap_Capability: function (capabilities, node) {
        var capability = {};
        this.runChildNodes(capability, node);
        capabilities.capability = capability;
    },
    read_cap_Request: function (obj, node) {
        var request = {};
        this.runChildNodes(request, node);
        obj.request = request;
    },
    read_cap_GetFeature: function (request, node) {
        var getfeature = {
            href: {},
            formats: []
        };
        this.runChildNodes(getfeature, node);
        request.getfeature = getfeature;
    },
    read_cap_ResultFormat: function (obj, node) {
        var children = node.childNodes;
        var childNode;
        for (var i = 0; i < children.length; i++) {
            childNode = children[i];
            if (childNode.nodeType == 1) {
                obj.formats.push(childNode.nodeName);
            }
        }
    },
    read_cap_DCPType: function (obj, node) {
        this.runChildNodes(obj, node);
    },
    read_cap_HTTP: function (obj, node) {
        this.runChildNodes(obj.href, node);
    },
    read_cap_Get: function (obj, node) {
        obj.get = node.getAttribute("onlineResource");
    },
    read_cap_Post: function (obj, node) {
        obj.post = node.getAttribute("onlineResource");
    },
    read_cap_SRS: function (obj, node) {
        var srs = this.getChildValue(node);
        if (srs) {
            obj.srs = srs;
        }
    },
    CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0"
});
OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, {
    defaultVersion: "1.1.1",
    profile: null,
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities"
});
OpenLayers.Format.WMSCapabilities.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        wms: "http://www.opengis.net/wms",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance"
    },
    defaultPrefix: "wms",
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var raw = data;
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var capabilities = {};
        this.readNode(data, capabilities);
        if (capabilities.service === undefined) {
            var parser = new OpenLayers.Format.OGCExceptionReport();
            capabilities.error = parser.read(raw);
        } else {
            this.postProcessLayers(capabilities);
        }
        return capabilities;
    },
    postProcessLayers: function (capabilities) {
        if (capabilities.capability) {
            capabilities.capability.layers = [];
            var layers = capabilities.capability.nestedLayers;
            for (var i = 0, len = layers.length; i < len; ++i) {
                var layer = layers[i];
                this.processLayer(capabilities.capability, layer);
            }
        }
    },
    processLayer: function (capability, layer, parentLayer) {
        if (layer.formats === undefined) {
            layer.formats = capability.request.getmap.formats;
        }
        var i, len;
        if (parentLayer) {
            layer.styles = layer.styles.concat(parentLayer.styles);
            var attributes = ["queryable", "cascaded", "fixedWidth", "fixedHeight", "opaque", "noSubsets", "llbbox", "minScale", "maxScale", "attribution"];
            var complexAttr = ["srs", "bbox", "dimensions", "authorityURLs"];
            var key;
            for (i = 0, len = attributes.length; i < len; i++) {
                key = attributes[i];
                if (key in parentLayer) {
                    if (layer[key] == null) {
                        layer[key] = parentLayer[key];
                    }
                    if (layer[key] == null) {
                        var intAttr = ["cascaded", "fixedWidth", "fixedHeight"];
                        var boolAttr = ["queryable", "opaque", "noSubsets"];
                        if (OpenLayers.Util.indexOf(intAttr, key) != -1) {
                            layer[key] = 0;
                        }
                        if (OpenLayers.Util.indexOf(boolAttr, key) != -1) {
                            layer[key] = false;
                        }
                    }
                }
            }
            for (i = 0, len = complexAttr.length; i < len; i++) {
                key = complexAttr[i];
                layer[key] = OpenLayers.Util.applyDefaults(layer[key], parentLayer[key]);
            }
        }
        for (i = 0, len = layer.nestedLayers.length; i < len; i++) {
            var childLayer = layer.nestedLayers[i];
            this.processLayer(capability, childLayer, layer);
        }
        if (layer.name) {
            capability.layers.push(layer);
        }
    },
    readers: {
        "wms": {
            "Service": function (node, obj) {
                obj.service = {};
                this.readChildNodes(node, obj.service);
            },
            "Name": function (node, obj) {
                obj.name = this.getChildValue(node);
            },
            "Title": function (node, obj) {
                obj.title = this.getChildValue(node);
            },
            "Abstract": function (node, obj) {
                obj["abstract"] = this.getChildValue(node);
            },
            "BoundingBox": function (node, obj) {
                var bbox = {};
                bbox.bbox = [parseFloat(node.getAttribute("minx")), parseFloat(node.getAttribute("miny")), parseFloat(node.getAttribute("maxx")), parseFloat(node.getAttribute("maxy"))];
                var res = {
                    x: parseFloat(node.getAttribute("resx")),
                    y: parseFloat(node.getAttribute("resy"))
                };
                if (!(isNaN(res.x) && isNaN(res.y))) {
                    bbox.res = res;
                }
                return bbox;
            },
            "OnlineResource": function (node, obj) {
                obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href");
            },
            "ContactInformation": function (node, obj) {
                obj.contactInformation = {};
                this.readChildNodes(node, obj.contactInformation);
            },
            "ContactPersonPrimary": function (node, obj) {
                obj.personPrimary = {};
                this.readChildNodes(node, obj.personPrimary);
            },
            "ContactPerson": function (node, obj) {
                obj.person = this.getChildValue(node);
            },
            "ContactOrganization": function (node, obj) {
                obj.organization = this.getChildValue(node);
            },
            "ContactPosition": function (node, obj) {
                obj.position = this.getChildValue(node);
            },
            "ContactAddress": function (node, obj) {
                obj.contactAddress = {};
                this.readChildNodes(node, obj.contactAddress);
            },
            "AddressType": function (node, obj) {
                obj.type = this.getChildValue(node);
            },
            "Address": function (node, obj) {
                obj.address = this.getChildValue(node);
            },
            "City": function (node, obj) {
                obj.city = this.getChildValue(node);
            },
            "StateOrProvince": function (node, obj) {
                obj.stateOrProvince = this.getChildValue(node);
            },
            "PostCode": function (node, obj) {
                obj.postcode = this.getChildValue(node);
            },
            "Country": function (node, obj) {
                obj.country = this.getChildValue(node);
            },
            "ContactVoiceTelephone": function (node, obj) {
                obj.phone = this.getChildValue(node);
            },
            "ContactFacsimileTelephone": function (node, obj) {
                obj.fax = this.getChildValue(node);
            },
            "ContactElectronicMailAddress": function (node, obj) {
                obj.email = this.getChildValue(node);
            },
            "Fees": function (node, obj) {
                var fees = this.getChildValue(node);
                if (fees && fees.toLowerCase() != "none") {
                    obj.fees = fees;
                }
            },
            "AccessConstraints": function (node, obj) {
                var constraints = this.getChildValue(node);
                if (constraints && constraints.toLowerCase() != "none") {
                    obj.accessConstraints = constraints;
                }
            },
            "Capability": function (node, obj) {
                obj.capability = {
                    nestedLayers: []
                };
                this.readChildNodes(node, obj.capability);
            },
            "Request": function (node, obj) {
                obj.request = {};
                this.readChildNodes(node, obj.request);
            },
            "GetCapabilities": function (node, obj) {
                obj.getcapabilities = {
                    formats: []
                };
                this.readChildNodes(node, obj.getcapabilities);
            },
            "Format": function (node, obj) {
                if (OpenLayers.Util.isArray(obj.formats)) {
                    obj.formats.push(this.getChildValue(node));
                } else {
                    obj.format = this.getChildValue(node);
                }
            },
            "DCPType": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "HTTP": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Get": function (node, obj) {
                obj.get = {};
                this.readChildNodes(node, obj.get);
                if (!obj.href) {
                    obj.href = obj.get.href;
                }
            },
            "Post": function (node, obj) {
                obj.post = {};
                this.readChildNodes(node, obj.post);
                if (!obj.href) {
                    obj.href = obj.get.href;
                }
            },
            "GetMap": function (node, obj) {
                obj.getmap = {
                    formats: []
                };
                this.readChildNodes(node, obj.getmap);
            },
            "GetFeatureInfo": function (node, obj) {
                obj.getfeatureinfo = {
                    formats: []
                };
                this.readChildNodes(node, obj.getfeatureinfo);
            },
            "Exception": function (node, obj) {
                obj.exception = {
                    formats: []
                };
                this.readChildNodes(node, obj.exception);
            },
            "Layer": function (node, obj) {
                var attrNode = node.getAttributeNode("queryable");
                var queryable = (attrNode && attrNode.specified) ? node.getAttribute("queryable") : null;
                attrNode = node.getAttributeNode("cascaded");
                var cascaded = (attrNode && attrNode.specified) ? node.getAttribute("cascaded") : null;
                attrNode = node.getAttributeNode("opaque");
                var opaque = (attrNode && attrNode.specified) ? node.getAttribute('opaque') : null;
                var noSubsets = node.getAttribute('noSubsets');
                var fixedWidth = node.getAttribute('fixedWidth');
                var fixedHeight = node.getAttribute('fixedHeight');
                var layer = {
                    nestedLayers: [],
                    styles: [],
                    srs: {},
                    metadataURLs: [],
                    bbox: {},
                    dimensions: {},
                    authorityURLs: {},
                    identifiers: {},
                    keywords: [],
                    queryable: (queryable && queryable !== "") ? (queryable === "1" || queryable === "true") : null,
                    cascaded: (cascaded !== null) ? parseInt(cascaded) : null,
                    opaque: opaque ? (opaque === "1" || opaque === "true") : null,
                    noSubsets: (noSubsets !== null) ? (noSubsets === "1" || noSubsets === "true") : null,
                    fixedWidth: (fixedWidth != null) ? parseInt(fixedWidth) : null,
                    fixedHeight: (fixedHeight != null) ? parseInt(fixedHeight) : null
                };
                obj.nestedLayers.push(layer);
                this.readChildNodes(node, layer);
                if (layer.name) {
                    var parts = layer.name.split(":");
                    if (parts.length > 0) {
                        layer.prefix = parts[0];
                    }
                }
            },
            "Attribution": function (node, obj) {
                obj.attribution = {};
                this.readChildNodes(node, obj.attribution);
            },
            "LogoURL": function (node, obj) {
                obj.logo = {
                    width: node.getAttribute("width"),
                    height: node.getAttribute("height")
                };
                this.readChildNodes(node, obj.logo);
            },
            "Style": function (node, obj) {
                var style = {};
                obj.styles.push(style);
                this.readChildNodes(node, style);
            },
            "LegendURL": function (node, obj) {
                var legend = {
                    width: node.getAttribute("width"),
                    height: node.getAttribute("height")
                };
                obj.legend = legend;
                this.readChildNodes(node, legend);
            },
            "MetadataURL": function (node, obj) {
                var metadataURL = {
                    type: node.getAttribute("type")
                };
                obj.metadataURLs.push(metadataURL);
                this.readChildNodes(node, metadataURL);
            },
            "DataURL": function (node, obj) {
                obj.dataURL = {};
                this.readChildNodes(node, obj.dataURL);
            },
            "FeatureListURL": function (node, obj) {
                obj.featureListURL = {};
                this.readChildNodes(node, obj.featureListURL);
            },
            "AuthorityURL": function (node, obj) {
                var name = node.getAttribute("name");
                var authority = {};
                this.readChildNodes(node, authority);
                obj.authorityURLs[name] = authority.href;
            },
            "Identifier": function (node, obj) {
                var authority = node.getAttribute("authority");
                obj.identifiers[authority] = this.getChildValue(node);
            },
            "KeywordList": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "SRS": function (node, obj) {
                obj.srs[this.getChildValue(node)] = true;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1"
});
OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1, {
    readers: {
        "wms": OpenLayers.Util.applyDefaults({
            "WMS_Capabilities": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "LayerLimit": function (node, obj) {
                obj.layerLimit = parseInt(this.getChildValue(node));
            },
            "MaxWidth": function (node, obj) {
                obj.maxWidth = parseInt(this.getChildValue(node));
            },
            "MaxHeight": function (node, obj) {
                obj.maxHeight = parseInt(this.getChildValue(node));
            },
            "BoundingBox": function (node, obj) {
                var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]);
                bbox.srs = node.getAttribute("CRS");
                obj.bbox[bbox.srs] = bbox;
            },
            "CRS": function (node, obj) {
                this.readers.wms.SRS.apply(this, [node, obj]);
            },
            "EX_GeographicBoundingBox": function (node, obj) {
                obj.llbbox = [];
                this.readChildNodes(node, obj.llbbox);
            },
            "westBoundLongitude": function (node, obj) {
                obj[0] = this.getChildValue(node);
            },
            "eastBoundLongitude": function (node, obj) {
                obj[2] = this.getChildValue(node);
            },
            "southBoundLatitude": function (node, obj) {
                obj[1] = this.getChildValue(node);
            },
            "northBoundLatitude": function (node, obj) {
                obj[3] = this.getChildValue(node);
            },
            "MinScaleDenominator": function (node, obj) {
                obj.maxScale = parseFloat(this.getChildValue(node)).toPrecision(16);
            },
            "MaxScaleDenominator": function (node, obj) {
                obj.minScale = parseFloat(this.getChildValue(node)).toPrecision(16);
            },
            "Dimension": function (node, obj) {
                var name = node.getAttribute("name").toLowerCase();
                var dim = {
                    name: name,
                    units: node.getAttribute("units"),
                    unitsymbol: node.getAttribute("unitSymbol"),
                    nearestVal: node.getAttribute("nearestValue") === "1",
                    multipleVal: node.getAttribute("multipleValues") === "1",
                    "default": node.getAttribute("default") || "",
                    current: node.getAttribute("current") === "1",
                    values: this.getChildValue(node).split(",")
                };
                obj.dimensions[dim.name] = dim;
            },
            "Keyword": function (node, obj) {
                var keyword = {
                    value: this.getChildValue(node),
                    vocabulary: node.getAttribute("vocabulary")
                };
                if (obj.keywords) {
                    obj.keywords.push(keyword);
                }
            }
        }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]),
        "sld": {
            "UserDefinedSymbolization": function (node, obj) {
                this.readers.wms.UserDefinedSymbolization.apply(this, [node, obj]);
                obj.userSymbols.inlineFeature = parseInt(node.getAttribute("InlineFeature")) == 1;
                obj.userSymbols.remoteWCS = parseInt(node.getAttribute("RemoteWCS")) == 1;
            },
            "DescribeLayer": function (node, obj) {
                this.readers.wms.DescribeLayer.apply(this, [node, obj]);
            },
            "GetLegendGraphic": function (node, obj) {
                this.readers.wms.GetLegendGraphic.apply(this, [node, obj]);
            }
        }
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3"
});
OpenLayers.Layer.Yahoo = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
    MIN_ZOOM_LEVEL: 0,
    MAX_ZOOM_LEVEL: 17,
    RESOLUTIONS: [1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 0.0000858306884765625, 0.00004291534423828125, 0.00002145767211914062, 0.00001072883605957031],
    type: null,
    wrapDateLine: true,
    sphericalMercator: false,
    initialize: function (name, options) {
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, arguments);
        if (this.sphericalMercator) {
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
            this.initMercatorParameters();
        }
    },
    loadMapObject: function () {
        try {
            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
            this.mapObject = new YMap(this.div, this.type, size);
            this.mapObject.disableKeyControls();
            this.mapObject.disableDragMap();
            if (!this.mapObject.moveByXY || (typeof this.mapObject.moveByXY != "function")) {
                this.dragPanMapObject = null;
            }
        } catch (e) {}
    },
    onMapResize: function () {
        try {
            var size = this.getMapObjectSizeFromOLSize(this.map.getSize());
            this.mapObject.resizeTo(size);
        } catch (e) {}
    },
    setMap: function (map) {
        OpenLayers.Layer.EventPane.prototype.setMap.apply(this, arguments);
        this.map.events.register("moveend", this, this.fixYahooEventPane);
    },
    fixYahooEventPane: function () {
        var yahooEventPane = OpenLayers.Util.getElement("ygddfdiv");
        if (yahooEventPane != null) {
            if (yahooEventPane.parentNode != null) {
                yahooEventPane.parentNode.removeChild(yahooEventPane);
            }
            this.map.events.unregister("moveend", this, this.fixYahooEventPane);
        }
    },
    getWarningHTML: function () {
        return OpenLayers.i18n("getLayerWarning", {
            'layerType': 'Yahoo',
            'layerLib': 'Yahoo'
        });
    },
    getOLZoomFromMapObjectZoom: function (moZoom) {
        var zoom = null;
        if (moZoom != null) {
            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getOLZoomFromMapObjectZoom.apply(this, [moZoom]);
            zoom = 18 - zoom;
        }
        return zoom;
    },
    getMapObjectZoomFromOLZoom: function (olZoom) {
        var zoom = null;
        if (olZoom != null) {
            zoom = OpenLayers.Layer.FixedZoomLevels.prototype.getMapObjectZoomFromOLZoom.apply(this, [olZoom]);
            zoom = 18 - zoom;
        }
        return zoom;
    },
    setMapObjectCenter: function (center, zoom) {
        this.mapObject.drawZoomAndCenter(center, zoom);
    },
    getMapObjectCenter: function () {
        return this.mapObject.getCenterLatLon();
    },
    dragPanMapObject: function (dX, dY) {
        this.mapObject.moveByXY({
            'x': -dX,
            'y': dY
        });
    },
    getMapObjectZoom: function () {
        return this.mapObject.getZoomLevel();
    },
    getMapObjectLonLatFromMapObjectPixel: function (moPixel) {
        return this.mapObject.convertXYLatLon(moPixel);
    },
    getMapObjectPixelFromMapObjectLonLat: function (moLonLat) {
        return this.mapObject.convertLatLonXY(moLonLat);
    },
    getLongitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lon : moLonLat.Lon;
    },
    getLatitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.Lon, moLonLat.Lat).lat : moLonLat.Lat;
    },
    getMapObjectLonLatFromLonLat: function (lon, lat) {
        var yLatLong;
        if (this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            yLatLong = new YGeoPoint(lonlat.lat, lonlat.lon);
        } else {
            yLatLong = new YGeoPoint(lat, lon);
        }
        return yLatLong;
    },
    getXFromMapObjectPixel: function (moPixel) {
        return moPixel.x;
    },
    getYFromMapObjectPixel: function (moPixel) {
        return moPixel.y;
    },
    getMapObjectPixelFromXY: function (x, y) {
        return new YCoordPoint(x, y);
    },
    getMapObjectSizeFromOLSize: function (olSize) {
        return new YSize(olSize.w, olSize.h);
    },
    CLASS_NAME: "OpenLayers.Layer.Yahoo"
});
OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, {
    url: null,
    size: null,
    isBaseLayer: true,
    standardTileSize: 256,
    tileOriginCorner: "tl",
    numberOfTiers: 0,
    tileCountUpToTier: new Array(),
    tierSizeInTiles: new Array(),
    tierImageSize: new Array(),
    initialize: function (name, url, size, options) {
        this.initializeZoomify(size);
        var newArguments = [];
        newArguments.push(name, url, size, {}, options);
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments);
    },
    initializeZoomify: function (size) {
        var imageSize = size.clone();
        var tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));
        this.tierSizeInTiles.push(tiles);
        this.tierImageSize.push(imageSize);
        while (imageSize.w > this.standardTileSize || imageSize.h > this.standardTileSize) {
            imageSize = new OpenLayers.Size(Math.floor(imageSize.w / 2), Math.floor(imageSize.h / 2));
            tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize));
            this.tierSizeInTiles.push(tiles);
            this.tierImageSize.push(imageSize);
        }
        this.tierSizeInTiles.reverse();
        this.tierImageSize.reverse();
        this.numberOfTiers = this.tierSizeInTiles.length;
        this.tileCountUpToTier[0] = 0;
        for (var i = 1; i < this.numberOfTiers; i++) {
            this.tileCountUpToTier.push(this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + this.tileCountUpToTier[i - 1]);
        }
    },
    destroy: function () {
        OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments);
        this.tileCountUpToTier.length = 0;
        this.tierSizeInTiles.length = 0;
        this.tierImageSize.length = 0;
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.Zoomify(this.name, this.url, this.size, this.options);
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
        var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();
        var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z];
        var path = "TileGroup" + Math.floor((tileIndex) / 256) + "/" + z + "-" + x + "-" + y + ".jpg";
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(path, url);
        }
        return url + path;
    },
    getImageSize: function () {
        if (arguments.length > 0) {
            var bounds = this.adjustBounds(arguments[0]);
            var res = this.map.getResolution();
            var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
            var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h));
            var z = this.map.getZoom();
            var w = this.standardTileSize;
            var h = this.standardTileSize;
            if (x == this.tierSizeInTiles[z].w - 1) {
                var w = this.tierImageSize[z].w % this.standardTileSize;
            };
            if (y == this.tierSizeInTiles[z].h - 1) {
                var h = this.tierImageSize[z].h % this.standardTileSize;
            };
            return (new OpenLayers.Size(w, h));
        } else {
            return this.tileSize;
        }
    },
    setMap: function (map) {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.top);
    },
    calculateGridLayout: function (bounds, origin, resolution) {
        var tilelon = resolution * this.tileSize.w;
        var tilelat = resolution * this.tileSize.h;
        var offsetlon = bounds.left - origin.lon;
        var tilecol = Math.floor(offsetlon / tilelon) - this.buffer;
        var tilecolremain = offsetlon / tilelon - tilecol;
        var tileoffsetx = -tilecolremain * this.tileSize.w;
        var tileoffsetlon = origin.lon + tilecol * tilelon;
        var offsetlat = origin.lat - bounds.top + tilelat;
        var tilerow = Math.floor(offsetlat / tilelat) - this.buffer;
        var tilerowremain = tilerow - offsetlat / tilelat;
        var tileoffsety = tilerowremain * this.tileSize.h;
        var tileoffsetlat = origin.lat - tilelat * tilerow;
        return {
            tilelon: tilelon,
            tilelat: tilelat,
            tileoffsetlon: tileoffsetlon,
            tileoffsetlat: tileoffsetlat,
            tileoffsetx: tileoffsetx,
            tileoffsety: tileoffsety
        };
    },
    CLASS_NAME: "OpenLayers.Layer.Zoomify"
});
OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, {
    xmlns: "urn:schemas-microsoft-com:vml",
    symbolCache: {},
    offset: null,
    initialize: function (containerID) {
        if (!this.supported()) {
            return;
        }
        if (!document.namespaces.olv) {
            document.namespaces.add("olv", this.xmlns);
            var style = document.createStyleSheet();
            var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox'];
            for (var i = 0, len = shapes.length; i < len; i++) {
                style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + "position: absolute; display: inline-block;");
            }
        }
        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);
    },
    supported: function () {
        return !!(document.namespaces);
    },
    setExtent: function (extent, resolutionChanged) {
        OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments);
        var resolution = this.getResolution();
        var left = (extent.left / resolution) | 0;
        var top = (extent.top / resolution - this.size.h) | 0;
        if (resolutionChanged || !this.offset) {
            this.offset = {
                x: left,
                y: top
            };
            left = 0;
            top = 0;
        } else {
            left = left - this.offset.x;
            top = top - this.offset.y;
        }
        var org = left + " " + top;
        this.root.coordorigin = org;
        var roots = [this.root, this.vectorRoot, this.textRoot];
        var root;
        for (var i = 0, len = roots.length; i < len; ++i) {
            root = roots[i];
            var size = this.size.w + " " + this.size.h;
            root.coordsize = size;
        }
        this.root.style.flip = "y";
        return true;
    },
    setSize: function (size) {
        OpenLayers.Renderer.prototype.setSize.apply(this, arguments);
        var roots = [this.rendererRoot, this.root, this.vectorRoot, this.textRoot];
        var w = this.size.w + "px";
        var h = this.size.h + "px";
        var root;
        for (var i = 0, len = roots.length; i < len; ++i) {
            root = roots[i];
            root.style.width = w;
            root.style.height = h;
        }
    },
    getNodeType: function (geometry, style) {
        var nodeType = null;
        switch (geometry.CLASS_NAME) {
        case "OpenLayers.Geometry.Point":
            if (style.externalGraphic) {
                nodeType = "olv:rect";
            } else if (this.isComplexSymbol(style.graphicName)) {
                nodeType = "olv:shape";
            } else {
                nodeType = "olv:oval";
            }
            break;
        case "OpenLayers.Geometry.Rectangle":
            nodeType = "olv:rect";
            break;
        case "OpenLayers.Geometry.LineString":
        case "OpenLayers.Geometry.LinearRing":
        case "OpenLayers.Geometry.Polygon":
        case "OpenLayers.Geometry.Curve":
        case "OpenLayers.Geometry.Surface":
            nodeType = "olv:shape";
            break;
        default:
            break;
        }
        return nodeType;
    },
    setStyle: function (node, style, options, geometry) {
        style = style || node._style;
        options = options || node._options;
        var fillColor = style.fillColor;
        if (node._geometryClass === "OpenLayers.Geometry.Point") {
            if (style.externalGraphic) {
                options.isFilled = true;
                if (style.graphicTitle) {
                    node.title = style.graphicTitle;
                }
                var width = style.graphicWidth || style.graphicHeight;
                var height = style.graphicHeight || style.graphicWidth;
                width = width ? width : style.pointRadius * 2;
                height = height ? height : style.pointRadius * 2;
                var resolution = this.getResolution();
                var xOffset = (style.graphicXOffset != undefined) ? style.graphicXOffset : -(0.5 * width);
                var yOffset = (style.graphicYOffset != undefined) ? style.graphicYOffset : -(0.5 * height);
                node.style.left = (((geometry.x / resolution - this.offset.x) + xOffset) | 0) + "px";
                node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + "px";
                node.style.width = width + "px";
                node.style.height = height + "px";
                node.style.flip = "y";
                fillColor = "none";
                options.isStroked = false;
            } else if (this.isComplexSymbol(style.graphicName)) {
                var cache = this.importSymbol(style.graphicName);
                node.path = cache.path;
                node.coordorigin = cache.left + "," + cache.bottom;
                var size = cache.size;
                node.coordsize = size + "," + size;
                this.drawCircle(node, geometry, style.pointRadius);
                node.style.flip = "y";
            } else {
                this.drawCircle(node, geometry, style.pointRadius);
            }
        }
        if (options.isFilled) {
            node.fillcolor = fillColor;
        } else {
            node.filled = "false";
        }
        var fills = node.getElementsByTagName("fill");
        var fill = (fills.length == 0) ? null : fills[0];
        if (!options.isFilled) {
            if (fill) {
                node.removeChild(fill);
            }
        } else {
            if (!fill) {
                fill = this.createNode('olv:fill', node.id + "_fill");
            }
            fill.opacity = style.fillOpacity;
            if (node._geometryClass === "OpenLayers.Geometry.Point" && style.externalGraphic) {
                if (style.graphicOpacity) {
                    fill.opacity = style.graphicOpacity;
                }
                fill.src = style.externalGraphic;
                fill.type = "frame";
                if (!(style.graphicWidth && style.graphicHeight)) {
                    fill.aspect = "atmost";
                }
            }
            if (fill.parentNode != node) {
                node.appendChild(fill);
            }
        }
        var rotation = style.rotation;
        if ((rotation !== undefined || node._rotation !== undefined)) {
            node._rotation = rotation;
            if (style.externalGraphic) {
                this.graphicRotate(node, xOffset, yOffset, style);
                fill.opacity = 0;
            } else if (node._geometryClass === "OpenLayers.Geometry.Point") {
                node.style.rotation = rotation || 0;
            }
        }
        var strokes = node.getElementsByTagName("stroke");
        var stroke = (strokes.length == 0) ? null : strokes[0];
        if (!options.isStroked) {
            node.stroked = false;
            if (stroke) {
                stroke.on = false;
            }
        } else {
            if (!stroke) {
                stroke = this.createNode('olv:stroke', node.id + "_stroke");
                node.appendChild(stroke);
            }
            stroke.on = true;
            stroke.color = style.strokeColor;
            stroke.weight = style.strokeWidth + "px";
            stroke.opacity = style.strokeOpacity;
            stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : (style.strokeLinecap || 'round');
            if (style.strokeDashstyle) {
                stroke.dashstyle = this.dashStyle(style);
            }
        }
        if (style.cursor != "inherit" && style.cursor != null) {
            node.style.cursor = style.cursor;
        }
        return node;
    },
    graphicRotate: function (node, xOffset, yOffset, style) {
        var style = style || node._style;
        var rotation = style.rotation || 0;
        var aspectRatio, size;
        if (!(style.graphicWidth && style.graphicHeight)) {
            var img = new Image();
            img.onreadystatechange = OpenLayers.Function.bind(function () {
                if (img.readyState == "complete" || img.readyState == "interactive") {
                    aspectRatio = img.width / img.height;
                    size = Math.max(style.pointRadius * 2, style.graphicWidth || 0, style.graphicHeight || 0);
                    xOffset = xOffset * aspectRatio;
                    style.graphicWidth = size * aspectRatio;
                    style.graphicHeight = size;
                    this.graphicRotate(node, xOffset, yOffset, style);
                }
            }, this);
            img.src = style.externalGraphic;
            return;
        } else {
            size = Math.max(style.graphicWidth, style.graphicHeight);
            aspectRatio = style.graphicWidth / style.graphicHeight;
        }
        var width = Math.round(style.graphicWidth || size * aspectRatio);
        var height = Math.round(style.graphicHeight || size);
        node.style.width = width + "px";
        node.style.height = height + "px";
        var image = document.getElementById(node.id + "_image");
        if (!image) {
            image = this.createNode("olv:imagedata", node.id + "_image");
            node.appendChild(image);
        }
        image.style.width = width + "px";
        image.style.height = height + "px";
        image.src = style.externalGraphic;
        image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + "src='', sizingMethod='scale')";
        var rot = rotation * Math.PI / 180;
        var sintheta = Math.sin(rot);
        var costheta = Math.cos(rot);
        var filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + ",SizingMethod='auto expand')\n";
        var opacity = style.graphicOpacity || style.fillOpacity;
        if (opacity && opacity != 1) {
            filter += "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + opacity + ")\n";
        }
        node.style.filter = filter;
        var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset);
        var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry();
        imgBox.rotate(style.rotation, centerPoint);
        var imgBounds = imgBox.getBounds();
        node.style.left = Math.round(parseInt(node.style.left) + imgBounds.left) + "px";
        node.style.top = Math.round(parseInt(node.style.top) - imgBounds.bottom) + "px";
    },
    postDraw: function (node) {
        node.style.visibility = "visible";
        var fillColor = node._style.fillColor;
        var strokeColor = node._style.strokeColor;
        if (fillColor == "none" && node.fillcolor != fillColor) {
            node.fillcolor = fillColor;
        }
        if (strokeColor == "none" && node.strokecolor != strokeColor) {
            node.strokecolor = strokeColor;
        }
    },
    setNodeDimension: function (node, geometry) {
        var bbox = geometry.getBounds();
        if (bbox) {
            var resolution = this.getResolution();
            var scaledBox = new OpenLayers.Bounds((bbox.left / resolution - this.offset.x) | 0, (bbox.bottom / resolution - this.offset.y) | 0, (bbox.right / resolution - this.offset.x) | 0, (bbox.top / resolution - this.offset.y) | 0);
            node.style.left = scaledBox.left + "px";
            node.style.top = scaledBox.top + "px";
            node.style.width = scaledBox.getWidth() + "px";
            node.style.height = scaledBox.getHeight() + "px";
            node.coordorigin = scaledBox.left + " " + scaledBox.top;
            node.coordsize = scaledBox.getWidth() + " " + scaledBox.getHeight();
        }
    },
    dashStyle: function (style) {
        var dash = style.strokeDashstyle;
        switch (dash) {
        case 'solid':
        case 'dot':
        case 'dash':
        case 'dashdot':
        case 'longdash':
        case 'longdashdot':
            return dash;
        default:
            var parts = dash.split(/[ ,]/);
            if (parts.length == 2) {
                if (1 * parts[0] >= 2 * parts[1]) {
                    return "longdash";
                }
                return (parts[0] == 1 || parts[1] == 1) ? "dot" : "dash";
            } else if (parts.length == 4) {
                return (1 * parts[0] >= 2 * parts[1]) ? "longdashdot" : "dashdot";
            }
            return "solid";
        }
    },
    createNode: function (type, id) {
        var node = document.createElement(type);
        if (id) {
            node.id = id;
        }
        node.unselectable = 'on';
        node.onselectstart = OpenLayers.Function.False;
        return node;
    },
    nodeTypeCompare: function (node, type) {
        var subType = type;
        var splitIndex = subType.indexOf(":");
        if (splitIndex != -1) {
            subType = subType.substr(splitIndex + 1);
        }
        var nodeName = node.nodeName;
        splitIndex = nodeName.indexOf(":");
        if (splitIndex != -1) {
            nodeName = nodeName.substr(splitIndex + 1);
        }
        return (subType == nodeName);
    },
    createRenderRoot: function () {
        return this.nodeFactory(this.container.id + "_vmlRoot", "div");
    },
    createRoot: function (suffix) {
        return this.nodeFactory(this.container.id + suffix, "olv:group");
    },
    drawPoint: function (node, geometry) {
        return this.drawCircle(node, geometry, 1);
    },
    drawCircle: function (node, geometry, radius) {
        if (!isNaN(geometry.x) && !isNaN(geometry.y)) {
            var resolution = this.getResolution();
            node.style.left = (((geometry.x / resolution - this.offset.x) | 0) - radius) + "px";
            node.style.top = (((geometry.y / resolution - this.offset.y) | 0) - radius) + "px";
            var diameter = radius * 2;
            node.style.width = diameter + "px";
            node.style.height = diameter + "px";
            return node;
        }
        return false;
    },
    drawLineString: function (node, geometry) {
        return this.drawLine(node, geometry, false);
    },
    drawLinearRing: function (node, geometry) {
        return this.drawLine(node, geometry, true);
    },
    drawLine: function (node, geometry, closeLine) {
        this.setNodeDimension(node, geometry);
        var resolution = this.getResolution();
        var numComponents = geometry.components.length;
        var parts = new Array(numComponents);
        var comp, x, y;
        for (var i = 0; i < numComponents; i++) {
            comp = geometry.components[i];
            x = (comp.x / resolution - this.offset.x) | 0;
            y = (comp.y / resolution - this.offset.y) | 0;
            parts[i] = " " + x + "," + y + " l ";
        }
        var end = (closeLine) ? " x e" : " e";
        node.path = "m" + parts.join("") + end;
        return node;
    },
    drawPolygon: function (node, geometry) {
        this.setNodeDimension(node, geometry);
        var resolution = this.getResolution();
        var path = [];
        var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y;
        for (j = 0, jj = geometry.components.length; j < jj; j++) {
            path.push("m");
            points = geometry.components[j].components;
            area = (j === 0);
            first = null;
            second = null;
            for (i = 0, ii = points.length; i < ii; i++) {
                comp = points[i];
                x = (comp.x / resolution - this.offset.x) | 0;
                y = (comp.y / resolution - this.offset.y) | 0;
                pathComp = " " + x + "," + y;
                path.push(pathComp);
                if (i == 0) {
                    path.push(" l");
                }
                if (!area) {
                    if (!first) {
                        first = pathComp;
                    } else if (first != pathComp) {
                        if (!second) {
                            second = pathComp;
                        } else if (second != pathComp) {
                            area = true;
                        }
                    }
                }
            }
            path.push(area ? " x " : " ");
        }
        path.push("e");
        node.path = path.join("");
        return node;
    },
    drawRectangle: function (node, geometry) {
        var resolution = this.getResolution();
        node.style.left = ((geometry.x / resolution - this.offset.x) | 0) + "px";
        node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + "px";
        node.style.width = ((geometry.width / resolution) | 0) + "px";
        node.style.height = ((geometry.height / resolution) | 0) + "px";
        return node;
    },
    drawText: function (featureId, style, location) {
        var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect");
        var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox");
        var resolution = this.getResolution();
        label.style.left = ((location.x / resolution - this.offset.x) | 0) + "px";
        label.style.top = ((location.y / resolution - this.offset.y) | 0) + "px";
        label.style.flip = "y";
        textbox.innerText = style.label;
        if (style.cursor != "inherit" && style.cursor != null) {
            textbox.style.cursor = style.cursor;
        }
        if (style.fontColor) {
            textbox.style.color = style.fontColor;
        }
        if (style.fontOpacity) {
            textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')';
        }
        if (style.fontFamily) {
            textbox.style.fontFamily = style.fontFamily;
        }
        if (style.fontSize) {
            textbox.style.fontSize = style.fontSize;
        }
        if (style.fontWeight) {
            textbox.style.fontWeight = style.fontWeight;
        }
        if (style.fontStyle) {
            textbox.style.fontStyle = style.fontStyle;
        }
        if (style.labelSelect === true) {
            label._featureId = featureId;
            textbox._featureId = featureId;
            textbox._geometry = location;
            textbox._geometryClass = location.CLASS_NAME;
        }
        textbox.style.whiteSpace = "nowrap";
        textbox.inset = "1px,0px,0px,0px";
        if (!label.parentNode) {
            label.appendChild(textbox);
            this.textRoot.appendChild(label);
        }
        var align = style.labelAlign || "cm";
        if (align.length == 1) {
            align += "m";
        }
        var xshift = textbox.clientWidth * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]);
        var yshift = textbox.clientHeight * (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]);
        label.style.left = parseInt(label.style.left) - xshift - 1 + "px";
        label.style.top = parseInt(label.style.top) + yshift + "px";
    },
    drawSurface: function (node, geometry) {
        this.setNodeDimension(node, geometry);
        var resolution = this.getResolution();
        var path = [];
        var comp, x, y;
        for (var i = 0, len = geometry.components.length; i < len; i++) {
            comp = geometry.components[i];
            x = (comp.x / resolution - this.offset.x) | 0;
            y = (comp.y / resolution - this.offset.y) | 0;
            if ((i % 3) == 0 && (i / 3) == 0) {
                path.push("m");
            } else if ((i % 3) == 1) {
                path.push(" c");
            }
            path.push(" " + x + "," + y);
        }
        path.push(" x e");
        node.path = path.join("");
        return node;
    },
    moveRoot: function (renderer) {
        var layer = this.map.getLayer(renderer.container.id);
        if (layer instanceof OpenLayers.Layer.Vector.RootContainer) {
            layer = this.map.getLayer(this.container.id);
        }
        layer && layer.renderer.clear();
        OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments);
        layer && layer.redraw();
    },
    importSymbol: function (graphicName) {
        var id = this.container.id + "-" + graphicName;
        var cache = this.symbolCache[id];
        if (cache) {
            return cache;
        }
        var symbol = OpenLayers.Renderer.symbol[graphicName];
        if (!symbol) {
            throw new Error(graphicName + ' is not a valid symbol name');
        }
        var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
        var pathitems = ["m"];
        for (var i = 0; i < symbol.length; i = i + 2) {
            var x = symbol[i];
            var y = symbol[i + 1];
            symbolExtent.left = Math.min(symbolExtent.left, x);
            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
            symbolExtent.right = Math.max(symbolExtent.right, x);
            symbolExtent.top = Math.max(symbolExtent.top, y);
            pathitems.push(x);
            pathitems.push(y);
            if (i == 0) {
                pathitems.push("l");
            }
        }
        pathitems.push("x e");
        var path = pathitems.join(" ");
        var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2;
        if (diff > 0) {
            symbolExtent.bottom = symbolExtent.bottom - diff;
            symbolExtent.top = symbolExtent.top + diff;
        } else {
            symbolExtent.left = symbolExtent.left + diff;
            symbolExtent.right = symbolExtent.right - diff;
        }
        cache = {
            path: path,
            size: symbolExtent.getWidth(),
            left: symbolExtent.left,
            bottom: symbolExtent.bottom
        };
        this.symbolCache[id] = cache;
        return cache;
    },
    CLASS_NAME: "OpenLayers.Renderer.VML"
});
OpenLayers.Renderer.VML.LABEL_SHIFT = {
    "l": 0,
    "c": .5,
    "r": 1,
    "t": 0,
    "m": .5,
    "b": 1
};
OpenLayers.Layer.MultiMap = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, {
    MIN_ZOOM_LEVEL: 1,
    MAX_ZOOM_LEVEL: 17,
    RESOLUTIONS: [9, 1.40625, 0.703125, 0.3515625, 0.17578125, 0.087890625, 0.0439453125, 0.02197265625, 0.010986328125, 0.0054931640625, 0.00274658203125, 0.001373291015625, 0.0006866455078125, 0.00034332275390625, 0.000171661376953125, 0.0000858306884765625, 0.00004291534423828125],
    type: null,
    initialize: function (name, options) {
        OpenLayers.Layer.EventPane.prototype.initialize.apply(this, arguments);
        OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, arguments);
        if (this.sphericalMercator) {
            OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator);
            this.initMercatorParameters();
            this.RESOLUTIONS.unshift(10);
        }
    },
    loadMapObject: function () {
        try {
            this.mapObject = new MultimapViewer(this.div);
        } catch (e) {}
    },
    getWarningHTML: function () {
        return OpenLayers.i18n("getLayerWarning", {
            'layerType': "MM",
            'layerLib': "MultiMap"
        });
    },
    setMapObjectCenter: function (center, zoom) {
        this.mapObject.goToPosition(center, zoom);
    },
    getMapObjectCenter: function () {
        return this.mapObject.getCurrentPosition();
    },
    getMapObjectZoom: function () {
        return this.mapObject.getZoomFactor();
    },
    getMapObjectLonLatFromMapObjectPixel: function (moPixel) {
        moPixel.x = moPixel.x - (this.map.getSize().w / 2);
        moPixel.y = moPixel.y - (this.map.getSize().h / 2);
        return this.mapObject.getMapPositionAt(moPixel);
    },
    getMapObjectPixelFromMapObjectLonLat: function (moLonLat) {
        return this.mapObject.geoPosToContainerPixels(moLonLat);
    },
    getLongitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.lon, moLonLat.lat).lon : moLonLat.lon;
    },
    getLatitudeFromMapObjectLonLat: function (moLonLat) {
        return this.sphericalMercator ? this.forwardMercator(moLonLat.lon, moLonLat.lat).lat : moLonLat.lat;
    },
    getMapObjectLonLatFromLonLat: function (lon, lat) {
        var mmLatLon;
        if (this.sphericalMercator) {
            var lonlat = this.inverseMercator(lon, lat);
            mmLatLon = new MMLatLon(lonlat.lat, lonlat.lon);
        } else {
            mmLatLon = new MMLatLon(lat, lon);
        }
        return mmLatLon;
    },
    getXFromMapObjectPixel: function (moPixel) {
        return moPixel.x;
    },
    getYFromMapObjectPixel: function (moPixel) {
        return moPixel.y;
    },
    getMapObjectPixelFromXY: function (x, y) {
        return new MMPoint(x, y);
    },
    CLASS_NAME: "OpenLayers.Layer.MultiMap"
});
OpenLayers.Control.MouseToolbar = OpenLayers.Class(OpenLayers.Control.MouseDefaults, {
    mode: null,
    buttons: null,
    direction: "vertical",
    buttonClicked: null,
    initialize: function (position, direction) {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X, OpenLayers.Control.MouseToolbar.Y);
        if (position) {
            this.position = position;
        }
        if (direction) {
            this.direction = direction;
        }
        this.measureDivs = [];
    },
    destroy: function () {
        for (var btnId in this.buttons) {
            var btn = this.buttons[btnId];
            btn.map = null;
            btn.events.destroy();
        }
        OpenLayers.Control.MouseDefaults.prototype.destroy.apply(this, arguments);
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
        this.buttons = {};
        var sz = new OpenLayers.Size(28, 28);
        var centered = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X, 0);
        this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
        this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
        this.switchModeTo("pan");
        return this.div;
    },
    _addButton: function (id, img, activeImg, xy, sz, title) {
        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
        var activeImgLocation = OpenLayers.Util.getImagesLocation() + activeImg;
        var btn = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MouseToolbar_" + id, xy, sz, imgLocation, "absolute");
        this.div.appendChild(btn);
        btn.imgLocation = imgLocation;
        btn.activeImgLocation = activeImgLocation;
        btn.events = new OpenLayers.Events(this, btn, null, true);
        btn.events.on({
            "mousedown": this.buttonDown,
            "mouseup": this.buttonUp,
            "dblclick": OpenLayers.Event.stop,
            scope: this
        });
        btn.action = id;
        btn.title = title;
        btn.alt = title;
        btn.map = this.map;
        this.buttons[id] = btn;
        return btn;
    },
    buttonDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        this.buttonClicked = evt.element.action;
        OpenLayers.Event.stop(evt);
    },
    buttonUp: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        if (this.buttonClicked != null) {
            if (this.buttonClicked == evt.element.action) {
                this.switchModeTo(evt.element.action);
            }
            OpenLayers.Event.stop(evt);
            this.buttonClicked = null;
        }
    },
    defaultDblClick: function (evt) {
        this.switchModeTo("pan");
        this.performedDrag = false;
        var newCenter = this.map.getLonLatFromViewPortPx(evt.xy);
        this.map.setCenter(newCenter, this.map.zoom + 1);
        OpenLayers.Event.stop(evt);
        return false;
    },
    defaultMouseDown: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        this.mouseDragStart = evt.xy.clone();
        this.performedDrag = false;
        this.startViaKeyboard = false;
        if (evt.shiftKey && this.mode != "zoombox") {
            this.switchModeTo("zoombox");
            this.startViaKeyboard = true;
        } else if (evt.altKey && this.mode != "measure") {
            this.switchModeTo("measure");
        } else if (!this.mode) {
            this.switchModeTo("pan");
        }
        switch (this.mode) {
        case "zoombox":
            this.map.div.style.cursor = "crosshair";
            this.zoomBox = OpenLayers.Util.createDiv('zoomBox', this.mouseDragStart, null, null, "absolute", "2px solid red");
            this.zoomBox.style.backgroundColor = "white";
            this.zoomBox.style.filter = "alpha(opacity=50)";
            this.zoomBox.style.opacity = "0.50";
            this.zoomBox.style.fontSize = "1px";
            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
            this.map.eventsDiv.appendChild(this.zoomBox);
            this.performedDrag = true;
            break;
        case "measure":
            var distance = "";
            if (this.measureStart) {
                var measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
                distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
                distance = Math.round(distance * 100) / 100;
                distance = distance + "km";
                this.measureStartBox = this.measureBox;
            }
            this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
            this.measureBox = OpenLayers.Util.createDiv(null, this.mouseDragStart.add(-2 - parseInt(this.map.layerContainerDiv.style.left), -2 - parseInt(this.map.layerContainerDiv.style.top)), null, null, "absolute");
            this.measureBox.style.width = "4px";
            this.measureBox.style.height = "4px";
            this.measureBox.style.fontSize = "1px";
            this.measureBox.style.backgroundColor = "red";
            this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
            this.map.layerContainerDiv.appendChild(this.measureBox);
            if (distance) {
                this.measureBoxDistance = OpenLayers.Util.createDiv(null, this.mouseDragStart.add(-2 - parseInt(this.map.layerContainerDiv.style.left), 2 - parseInt(this.map.layerContainerDiv.style.top)), null, null, "absolute");
                this.measureBoxDistance.innerHTML = distance;
                this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
                this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
                this.measureDivs.push(this.measureBoxDistance);
            }
            this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
            this.map.layerContainerDiv.appendChild(this.measureBox);
            this.measureDivs.push(this.measureBox);
            break;
        default:
            this.map.div.style.cursor = "move";
            break;
        }
        document.onselectstart = OpenLayers.Function.False;
        OpenLayers.Event.stop(evt);
    },
    switchModeTo: function (mode) {
        if (mode != this.mode) {
            if (this.mode && this.buttons[this.mode]) {
                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
            }
            if (this.mode == "measure" && mode != "measure") {
                for (var i = 0, len = this.measureDivs.length; i < len; i++) {
                    if (this.measureDivs[i]) {
                        this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
                    }
                }
                this.measureDivs = [];
                this.measureStart = null;
            }
            this.mode = mode;
            if (this.buttons[mode]) {
                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
            }
            switch (this.mode) {
            case "zoombox":
                this.map.div.style.cursor = "crosshair";
                break;
            default:
                this.map.div.style.cursor = "";
                break;
            }
        }
    },
    leaveMode: function () {
        this.switchModeTo("pan");
    },
    defaultMouseMove: function (evt) {
        if (this.mouseDragStart != null) {
            switch (this.mode) {
            case "zoombox":
                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
                if (evt.xy.x < this.mouseDragStart.x) {
                    this.zoomBox.style.left = evt.xy.x + "px";
                }
                if (evt.xy.y < this.mouseDragStart.y) {
                    this.zoomBox.style.top = evt.xy.y + "px";
                }
                break;
            default:
                var deltaX = this.mouseDragStart.x - evt.xy.x;
                var deltaY = this.mouseDragStart.y - evt.xy.y;
                var size = this.map.getSize();
                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX, size.h / 2 + deltaY);
                var newCenter = this.map.getLonLatFromViewPortPx(newXY);
                this.map.setCenter(newCenter, null, true);
                this.mouseDragStart = evt.xy.clone();
            }
            this.performedDrag = true;
        }
    },
    defaultMouseUp: function (evt) {
        if (!OpenLayers.Event.isLeftClick(evt)) {
            return;
        }
        switch (this.mode) {
        case "zoombox":
            this.zoomBoxEnd(evt);
            if (this.startViaKeyboard) {
                this.leaveMode();
            }
            break;
        case "pan":
            if (this.performedDrag) {
                this.map.setCenter(this.map.center);
            }
        }
        document.onselectstart = null;
        this.mouseDragStart = null;
        this.map.div.style.cursor = "default";
    },
    defaultMouseOut: function (evt) {
        if (this.mouseDragStart != null && OpenLayers.Util.mouseLeft(evt, this.map.eventsDiv)) {
            if (this.zoomBox) {
                this.removeZoomBox();
                if (this.startViaKeyboard) {
                    this.leaveMode();
                }
            }
            this.mouseDragStart = null;
            this.map.div.style.cursor = "default";
        }
    },
    defaultClick: function (evt) {
        if (this.performedDrag) {
            this.performedDrag = false;
            return false;
        }
    },
    CLASS_NAME: "OpenLayers.Control.MouseToolbar"
});
OpenLayers.Control.MouseToolbar.X = 6;
OpenLayers.Control.MouseToolbar.Y = 300;
OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
    version: "1.0.0",
    CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0"
});
OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, {
    layerIdentifier: '_layer',
    featureIdentifier: '_feature',
    regExes: {
        trimSpace: (/^\s*|\s*$/g),
        removeSpace: (/\s*/g),
        splitSpace: (/\s+/),
        trimComma: (/\s*,\s*/g)
    },
    gmlFormat: null,
    read: function (data) {
        var result;
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        var root = data.documentElement;
        if (root) {
            var scope = this;
            var read = this["read_" + root.nodeName];
            if (read) {
                result = read.call(this, root);
            } else {
                result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data);
            }
        } else {
            result = data;
        }
        return result;
    },
    read_msGMLOutput: function (data) {
        var response = [];
        var layerNodes = this.getSiblingNodesByTagCriteria(data, this.layerIdentifier);
        if (layerNodes) {
            for (var i = 0, len = layerNodes.length; i < len; ++i) {
                var node = layerNodes[i];
                var layerName = node.nodeName;
                if (node.prefix) {
                    layerName = layerName.split(':')[1];
                }
                var layerName = layerName.replace(this.layerIdentifier, '');
                var featureNodes = this.getSiblingNodesByTagCriteria(node, this.featureIdentifier);
                if (featureNodes) {
                    for (var j = 0; j < featureNodes.length; j++) {
                        var featureNode = featureNodes[j];
                        var geomInfo = this.parseGeometry(featureNode);
                        var attributes = this.parseAttributes(featureNode);
                        var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, attributes, null);
                        feature.bounds = geomInfo.bounds;
                        feature.type = layerName;
                        response.push(feature);
                    }
                }
            }
        }
        return response;
    },
    read_FeatureInfoResponse: function (data) {
        var response = [];
        var featureNodes = this.getElementsByTagNameNS(data, '*', 'FIELDS');
        for (var i = 0, len = featureNodes.length; i < len; i++) {
            var featureNode = featureNodes[i];
            var geom = null;
            var attributes = {};
            var j;
            var jlen = featureNode.attributes.length;
            if (jlen > 0) {
                for (j = 0; j < jlen; j++) {
                    var attribute = featureNode.attributes[j];
                    attributes[attribute.nodeName] = attribute.nodeValue;
                }
            } else {
                var nodes = featureNode.childNodes;
                for (j = 0, jlen = nodes.length; j < jlen; ++j) {
                    var node = nodes[j];
                    if (node.nodeType != 3) {
                        attributes[node.getAttribute("name")] = node.getAttribute("value");
                    }
                }
            }
            response.push(new OpenLayers.Feature.Vector(geom, attributes, null));
        }
        return response;
    },
    getSiblingNodesByTagCriteria: function (node, criteria) {
        var nodes = [];
        var children, tagName, n, matchNodes, child;
        if (node && node.hasChildNodes()) {
            children = node.childNodes;
            n = children.length;
            for (var k = 0; k < n; k++) {
                child = children[k];
                while (child && child.nodeType != 1) {
                    child = child.nextSibling;
                    k++;
                }
                tagName = (child ? child.nodeName : '');
                if (tagName.length > 0 && tagName.indexOf(criteria) > -1) {
                    nodes.push(child);
                } else {
                    matchNodes = this.getSiblingNodesByTagCriteria(child, criteria);
                    if (matchNodes.length > 0) {
                        (nodes.length == 0) ? nodes = matchNodes : nodes.push(matchNodes);
                    }
                }
            }
        }
        return nodes;
    },
    parseAttributes: function (node) {
        var attributes = {};
        if (node.nodeType == 1) {
            var children = node.childNodes;
            var n = children.length;
            for (var i = 0; i < n; ++i) {
                var child = children[i];
                if (child.nodeType == 1) {
                    var grandchildren = child.childNodes;
                    var name = (child.prefix) ? child.nodeName.split(":")[1] : child.nodeName;
                    if (grandchildren.length == 0) {
                        attributes[name] = null
                    } else if (grandchildren.length == 1) {
                        var grandchild = grandchildren[0];
                        if (grandchild.nodeType == 3 || grandchild.nodeType == 4) {
                            var value = grandchild.nodeValue.replace(this.regExes.trimSpace, "");
                            attributes[name] = value;
                        }
                    }
                }
            }
        }
        return attributes;
    },
    parseGeometry: function (node) {
        if (!this.gmlFormat) {
            this.gmlFormat = new OpenLayers.Format.GML();
        }
        var feature = this.gmlFormat.parseFeature(node);
        var geometry, bounds = null;
        if (feature) {
            geometry = feature.geometry && feature.geometry.clone();
            bounds = feature.bounds && feature.bounds.clone();
            feature.destroy();
        }
        return {
            geometry: geometry,
            bounds: bounds
        };
    },
    CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo"
});
OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
    hover: false,
    requestEncoding: "KVP",
    drillDown: false,
    maxFeatures: 10,
    clickCallback: "click",
    layers: null,
    queryVisible: true,
    infoFormat: 'text/html',
    vendorParams: {},
    format: null,
    formatOptions: null,
    handlerOptions: null,
    handler: null,
    hoverRequest: null,
    EVENT_TYPES: ["beforegetfeatureinfo", "getfeatureinfo", "exception"],
    pending: 0,
    initialize: function (options) {
        this.EVENT_TYPES = OpenLayers.Control.WMTSGetFeatureInfo.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        options = options || {};
        options.handlerOptions = options.handlerOptions || {};
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        if (!this.format) {
            this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions);
        }
        if (this.drillDown === true) {
            this.hover = false;
        }
        if (this.hover) {
            this.handler = new OpenLayers.Handler.Hover(this, {
                move: this.cancelHover,
                pause: this.getInfoForHover
            }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, {
                delay: 250
            }));
        } else {
            var callbacks = {};
            callbacks[this.clickCallback] = this.getInfoForClick;
            this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {});
        }
    },
    getInfoForClick: function (evt) {
        this.request(evt.xy, {});
    },
    getInfoForHover: function (evt) {
        this.request(evt.xy, {
            hover: true
        });
    },
    cancelHover: function () {
        if (this.hoverRequest) {
            --this.pending;
            if (this.pending <= 0) {
                OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
                this.pending = 0;
            }
            this.hoverRequest.abort();
            this.hoverRequest = null;
        }
    },
    findLayers: function () {
        var candidates = this.layers || this.map.layers;
        var layers = [];
        var layer;
        for (var i = candidates.length - 1; i >= 0; --i) {
            layer = candidates[i];
            if (layer instanceof OpenLayers.Layer.WMTS && layer.requestEncoding === this.requestEncoding && (!this.queryVisible || layer.getVisibility())) {
                layers.push(layer);
                if (!this.drillDown || this.hover) {
                    break;
                }
            }
        }
        return layers;
    },
    buildRequestOptions: function (layer, xy) {
        var loc = this.map.getLonLatFromPixel(xy);
        var getTileUrl = layer.getURL(new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat));
        var params = OpenLayers.Util.getParameters(getTileUrl);
        var tileInfo = layer.getTileInfo(loc);
        OpenLayers.Util.extend(params, {
            service: "WMTS",
            version: layer.version,
            request: "GetFeatureInfo",
            infoFormat: this.infoFormat,
            i: tileInfo.i,
            j: tileInfo.j
        });
        OpenLayers.Util.applyDefaults(params, this.vendorParams);
        return {
            url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url,
            params: OpenLayers.Util.upperCaseObject(params),
            callback: function (request) {
                this.handleResponse(xy, request, layer);
            },
            scope: this
        };
    },
    request: function (xy, options) {
        options = options || {};
        var layers = this.findLayers();
        if (layers.length > 0) {
            var issue, layer;
            for (var i = 0, len = layers.length; i < len; i++) {
                layer = layers[i];
                issue = this.events.triggerEvent("beforegetfeatureinfo", {
                    xy: xy,
                    layer: layer
                });
                if (issue !== false) {
                    ++this.pending;
                    var requestOptions = this.buildRequestOptions(layer, xy);
                    var request = OpenLayers.Request.GET(requestOptions);
                    if (options.hover === true) {
                        this.hoverRequest = request;
                    }
                }
            }
            if (this.pending > 0) {
                OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
            }
        }
    },
    handleResponse: function (xy, request, layer) {
        --this.pending;
        if (this.pending <= 0) {
            OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
            this.pending = 0;
        }
        if (request.status && (request.status < 200 || request.status >= 300)) {
            this.events.triggerEvent("exception", {
                xy: xy,
                request: request,
                layer: layer
            });
        } else {
            var doc = request.responseXML;
            if (!doc || !doc.documentElement) {
                doc = request.responseText;
            }
            var features, except;
            try {
                features = this.format.read(doc);
            } catch (error) {
                except = true;
                this.events.triggerEvent("exception", {
                    xy: xy,
                    request: request,
                    error: error,
                    layer: layer
                });
            }
            if (!except) {
                this.events.triggerEvent("getfeatureinfo", {
                    text: request.responseText,
                    features: features,
                    request: request,
                    xy: xy,
                    layer: layer
                });
            }
        }
    },
    CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo"
});
OpenLayers.Format.WMSCapabilities.v1_1 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1, {
    readers: {
        "wms": OpenLayers.Util.applyDefaults({
            "WMT_MS_Capabilities": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Keyword": function (node, obj) {
                if (obj.keywords) {
                    obj.keywords.push(this.getChildValue(node));
                }
            },
            "DescribeLayer": function (node, obj) {
                obj.describelayer = {
                    formats: []
                };
                this.readChildNodes(node, obj.describelayer);
            },
            "GetLegendGraphic": function (node, obj) {
                obj.getlegendgraphic = {
                    formats: []
                };
                this.readChildNodes(node, obj.getlegendgraphic);
            },
            "GetStyles": function (node, obj) {
                obj.getstyles = {
                    formats: []
                };
                this.readChildNodes(node, obj.getstyles);
            },
            "PutStyles": function (node, obj) {
                obj.putstyles = {
                    formats: []
                };
                this.readChildNodes(node, obj.putstyles);
            },
            "UserDefinedSymbolization": function (node, obj) {
                var userSymbols = {
                    supportSLD: parseInt(node.getAttribute("SupportSLD")) == 1,
                    userLayer: parseInt(node.getAttribute("UserLayer")) == 1,
                    userStyle: parseInt(node.getAttribute("UserStyle")) == 1,
                    remoteWFS: parseInt(node.getAttribute("RemoteWFS")) == 1
                };
                obj.userSymbols = userSymbols;
            },
            "LatLonBoundingBox": function (node, obj) {
                obj.llbbox = [parseFloat(node.getAttribute("minx")), parseFloat(node.getAttribute("miny")), parseFloat(node.getAttribute("maxx")), parseFloat(node.getAttribute("maxy"))];
            },
            "BoundingBox": function (node, obj) {
                var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]);
                bbox.srs = node.getAttribute("SRS");
                obj.bbox[bbox.srs] = bbox;
            },
            "ScaleHint": function (node, obj) {
                var min = node.getAttribute("min");
                var max = node.getAttribute("max");
                var rad2 = Math.pow(2, 0.5);
                var ipm = OpenLayers.INCHES_PER_UNIT["m"];
                obj.maxScale = parseFloat(((min / rad2) * ipm * OpenLayers.DOTS_PER_INCH).toPrecision(13));
                obj.minScale = parseFloat(((max / rad2) * ipm * OpenLayers.DOTS_PER_INCH).toPrecision(13));
            },
            "Dimension": function (node, obj) {
                var name = node.getAttribute("name").toLowerCase();
                var dim = {
                    name: name,
                    units: node.getAttribute("units"),
                    unitsymbol: node.getAttribute("unitSymbol")
                };
                obj.dimensions[dim.name] = dim;
            },
            "Extent": function (node, obj) {
                var name = node.getAttribute("name").toLowerCase();
                if (name in obj["dimensions"]) {
                    var extent = obj.dimensions[name];
                    extent.nearestVal = node.getAttribute("nearestValue") === "1";
                    extent.multipleVal = node.getAttribute("multipleValues") === "1";
                    extent.current = node.getAttribute("current") === "1";
                    extent["default"] = node.getAttribute("default") || "";
                    var values = this.getChildValue(node);
                    extent.values = values.split(",");
                }
            }
        }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"])
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1"
});
OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, {
    autoActivate: true,
    intervals: [45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01, 0.005, 0.002, 0.001],
    displayInLayerSwitcher: true,
    visible: true,
    numPoints: 50,
    targetSize: 200,
    layerName: null,
    labelled: true,
    labelFormat: 'dm',
    lineSymbolizer: {
        strokeColor: "#333",
        strokeWidth: 1,
        strokeOpacity: 0.5
    },
    labelSymbolizer: {},
    gratLayer: null,
    initialize: function (options) {
        options = options || {};
        options.layerName = options.layerName || OpenLayers.i18n("Graticule");
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.labelSymbolizer.stroke = false;
        this.labelSymbolizer.fill = false;
        this.labelSymbolizer.label = "${label}";
        this.labelSymbolizer.labelAlign = "${labelAlign}";
        this.labelSymbolizer.labelXOffset = "${xOffset}";
        this.labelSymbolizer.labelYOffset = "${yOffset}";
    },
    destroy: function () {
        this.deactivate();
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
        if (this.gratLayer) {
            this.gratLayer.destroy();
            this.gratLayer = null;
        }
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        if (!this.gratLayer) {
            var gratStyle = new OpenLayers.Style({}, {
                rules: [new OpenLayers.Rule({
                    'symbolizer': {
                        "Point": this.labelSymbolizer,
                        "Line": this.lineSymbolizer
                    }
                })]
            });
            this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, {
                styleMap: new OpenLayers.StyleMap({
                    'default': gratStyle
                }),
                visibility: this.visible,
                displayInLayerSwitcher: this.displayInLayerSwitcher
            });
        }
        return this.div;
    },
    activate: function () {
        if (OpenLayers.Control.prototype.activate.apply(this, arguments)) {
            this.map.addLayer(this.gratLayer);
            this.map.events.register('moveend', this, this.update);
            this.update();
            return true;
        } else {
            return false;
        }
    },
    deactivate: function () {
        if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.map.events.unregister('moveend', this, this.update);
            this.map.removeLayer(this.gratLayer);
            return true;
        } else {
            return false;
        }
    },
    update: function () {
        var mapBounds = this.map.getExtent();
        if (!mapBounds) {
            return;
        }
        this.gratLayer.destroyFeatures();
        var llProj = new OpenLayers.Projection("EPSG:4326");
        var mapProj = this.map.getProjectionObject();
        var mapRes = this.map.getResolution();
        if (mapProj.proj && mapProj.proj.projName == "longlat") {
            this.numPoints = 1;
        }
        var mapCenter = this.map.getCenter();
        var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat);
        OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj);
        var testSq = this.targetSize * mapRes;
        testSq *= testSq;
        var llInterval;
        for (var i = 0; i < this.intervals.length; ++i) {
            llInterval = this.intervals[i];
            var delta = llInterval / 2;
            var p1 = mapCenterLL.offset(new OpenLayers.Pixel(-delta, -delta));
            var p2 = mapCenterLL.offset(new OpenLayers.Pixel(delta, delta));
            OpenLayers.Projection.transform(p1, llProj, mapProj);
            OpenLayers.Projection.transform(p2, llProj, mapProj);
            var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y);
            if (distSq <= testSq) {
                break;
            }
        }
        mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval;
        mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval;
        var iter = 0;
        var centerLonPoints = [mapCenterLL.clone()];
        var newPoint = mapCenterLL.clone();
        var mapXY;
        do {
            newPoint = newPoint.offset(new OpenLayers.Pixel(0, llInterval));
            mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
            centerLonPoints.unshift(newPoint);
        } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);
        newPoint = mapCenterLL.clone();
        do {
            newPoint = newPoint.offset(new OpenLayers.Pixel(0, -llInterval));
            mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
            centerLonPoints.push(newPoint);
        } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);
        iter = 0;
        var centerLatPoints = [mapCenterLL.clone()];
        newPoint = mapCenterLL.clone();
        do {
            newPoint = newPoint.offset(new OpenLayers.Pixel(-llInterval, 0));
            mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
            centerLatPoints.unshift(newPoint);
        } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);
        newPoint = mapCenterLL.clone();
        do {
            newPoint = newPoint.offset(new OpenLayers.Pixel(llInterval, 0));
            mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj);
            centerLatPoints.push(newPoint);
        } while (mapBounds.containsPixel(mapXY) && ++iter < 1000);
        var lines = [];
        for (var i = 0; i < centerLatPoints.length; ++i) {
            var lon = centerLatPoints[i].x;
            var pointList = [];
            var labelPoint = null;
            var latEnd = Math.min(centerLonPoints[0].y, 90);
            var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90);
            var latDelta = (latEnd - latStart) / this.numPoints;
            var lat = latStart;
            for (var j = 0; j <= this.numPoints; ++j) {
                var gridPoint = new OpenLayers.Geometry.Point(lon, lat);
                gridPoint.transform(llProj, mapProj);
                pointList.push(gridPoint);
                lat += latDelta;
                if (gridPoint.y >= mapBounds.bottom && !labelPoint) {
                    labelPoint = gridPoint;
                }
            }
            if (this.labelled) {
                var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom);
                var labelAttrs = {
                    value: lon,
                    label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat) : "",
                    labelAlign: "cb",
                    xOffset: 0,
                    yOffset: 2
                };
                this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));
            }
            var geom = new OpenLayers.Geometry.LineString(pointList);
            lines.push(new OpenLayers.Feature.Vector(geom));
        }
        for (var j = 0; j < centerLonPoints.length; ++j) {
            lat = centerLonPoints[j].y;
            if (lat < -90 || lat > 90) {
                continue;
            }
            var pointList = [];
            var lonStart = centerLatPoints[0].x;
            var lonEnd = centerLatPoints[centerLatPoints.length - 1].x;
            var lonDelta = (lonEnd - lonStart) / this.numPoints;
            var lon = lonStart;
            var labelPoint = null;
            for (var i = 0; i <= this.numPoints; ++i) {
                var gridPoint = new OpenLayers.Geometry.Point(lon, lat);
                gridPoint.transform(llProj, mapProj);
                pointList.push(gridPoint);
                lon += lonDelta;
                if (gridPoint.x < mapBounds.right) {
                    labelPoint = gridPoint;
                }
            }
            if (this.labelled) {
                var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y);
                var labelAttrs = {
                    value: lat,
                    label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat) : "",
                    labelAlign: "rb",
                    xOffset: -2,
                    yOffset: 2
                };
                this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs));
            }
            var geom = new OpenLayers.Geometry.LineString(pointList);
            lines.push(new OpenLayers.Feature.Vector(geom));
        }
        this.gratLayer.addFeatures(lines);
    },
    CLASS_NAME: "OpenLayers.Control.Graticule"
});
OpenLayers.Layer.WMS.Post = OpenLayers.Class(OpenLayers.Layer.WMS, {
    unsupportedBrowsers: ["mozilla", "firefox", "opera"],
    SUPPORTED_TRANSITIONS: [],
    usePost: null,
    initialize: function (name, url, params, options) {
        var newArguments = [];
        newArguments.push(name, url, params, options);
        OpenLayers.Layer.WMS.prototype.initialize.apply(this, newArguments);
        this.usePost = OpenLayers.Util.indexOf(this.unsupportedBrowsers, OpenLayers.BROWSER_NAME) == -1;
    },
    addTile: function (bounds, position) {
        return new OpenLayers.Tile.Image(this, position, bounds, null, this.tileSize, {
            maxGetUrlLength: this.usePost ? 0 : null
        });
    },
    CLASS_NAME: 'OpenLayers.Layer.WMS.Post'
});
OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {
    url: null,
    tileOrigin: null,
    tileSize: new OpenLayers.Size(256, 256),
    useArcGISServer: true,
    type: 'png',
    useScales: false,
    overrideDPI: false,
    initialize: function (name, url, options) {
        OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
        if (this.resolutions) {
            this.serverResolutions = this.resolutions;
            this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);
        }
        if (this.layerInfo) {
            var info = this.layerInfo;
            var startingTileExtent = new OpenLayers.Bounds(info.fullExtent.xmin, info.fullExtent.ymin, info.fullExtent.xmax, info.fullExtent.ymax);
            this.projection = 'EPSG:' + info.spatialReference.wkid;
            this.sphericalMercator = (info.spatialReference.wkid == 102100);
            this.units = (info.units == "esriFeet") ? 'ft' : 'm';
            if ( !! info.tileInfo) {
                this.tileSize = new OpenLayers.Size(info.tileInfo.width || info.tileInfo.cols, info.tileInfo.height || info.tileInfo.rows);
                this.tileOrigin = new OpenLayers.LonLat(info.tileInfo.origin.x, info.tileInfo.origin.y);
                var upperLeft = new OpenLayers.Geometry.Point(startingTileExtent.left, startingTileExtent.top);
                var bottomRight = new OpenLayers.Geometry.Point(startingTileExtent.right, startingTileExtent.bottom);
                if (this.useScales) {
                    this.scales = [];
                } else {
                    this.resolutions = [];
                }
                this.lods = [];
                for (var key in info.tileInfo.lods) {
                    if (info.tileInfo.lods.hasOwnProperty(key)) {
                        var lod = info.tileInfo.lods[key];
                        if (this.useScales) {
                            this.scales.push(lod.scale);
                        } else {
                            this.resolutions.push(lod.resolution);
                        }
                        var start = this.getContainingTileCoords(upperLeft, lod.resolution);
                        lod.startTileCol = start.x;
                        lod.startTileRow = start.y;
                        var end = this.getContainingTileCoords(bottomRight, lod.resolution);
                        lod.endTileCol = end.x;
                        lod.endTileRow = end.y;
                        this.lods.push(lod);
                    }
                }
                this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);
                this.serverResolutions = this.resolutions;
                if (this.overrideDPI && info.tileInfo.dpi) {
                    OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;
                }
            }
        }
    },
    getContainingTileCoords: function (point, res) {
        return new OpenLayers.Pixel(Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0));
    },
    calculateMaxExtentWithLOD: function (lod) {
        var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;
        var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;
        var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);
        var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);
        var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);
        var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);
        return new OpenLayers.Bounds(minX, minY, maxX, maxY);
    },
    calculateMaxExtentWithExtent: function (extent, res) {
        var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);
        var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);
        var start = this.getContainingTileCoords(upperLeft, res);
        var end = this.getContainingTileCoords(bottomRight, res);
        var lod = {
            resolution: res,
            startTileCol: start.x,
            startTileRow: start.y,
            endTileCol: end.x,
            endTileRow: end.y
        };
        return this.calculateMaxExtentWithLOD(lod);
    },
    getUpperLeftTileCoord: function (res) {
        var upperLeft = new OpenLayers.Geometry.Point(this.maxExtent.left, this.maxExtent.top);
        return this.getContainingTileCoords(upperLeft, res);
    },
    getLowerRightTileCoord: function (res) {
        var bottomRight = new OpenLayers.Geometry.Point(this.maxExtent.right, this.maxExtent.bottom);
        return this.getContainingTileCoords(bottomRight, res);
    },
    getMaxExtentForResolution: function (res) {
        var start = this.getUpperLeftTileCoord(res);
        var end = this.getLowerRightTileCoord(res);
        var numTileCols = (end.x - start.x) + 1;
        var numTileRows = (end.y - start.y) + 1;
        var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);
        var maxX = minX + (numTileCols * this.tileSize.w * res);
        var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);
        var minY = maxY - (numTileRows * this.tileSize.h * res);
        return new OpenLayers.Bounds(minX, minY, maxX, maxY);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);
        }
        return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
    },
    getMaxExtent: function () {
        var resolution = this.map.getResolution();
        return this.maxExtent = this.getMaxExtentForResolution(resolution);
    },
    getTileOrigin: function () {
        var extent = this.getMaxExtent();
        return new OpenLayers.LonLat(extent.left, extent.bottom);
    },
    getURL: function (bounds) {
        var res = this.getResolution();
        var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2));
        var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2));
        var center = bounds.getCenterLonLat();
        var point = {
            x: center.lon,
            y: center.lat
        };
        var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))));
        var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))));
        var z = this.map.getZoom();
        if (this.lods) {
            var lod = this.lods[this.map.getZoom()];
            if ((x < lod.startTileCol || x > lod.endTileCol) || (y < lod.startTileRow || y > lod.endTileRow)) {
                return null;
            }
        } else {
            var start = this.getUpperLeftTileCoord(res);
            var end = this.getLowerRightTileCoord(res);
            if ((x < start.x || x >= end.x) || (y < start.y || y >= end.y)) {
                return null;
            }
        }
        var url = this.url;
        var s = '' + x + y + z;
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(s, url);
        }
        if (this.useArcGISServer) {
            url = url + '/tile/${z}/${y}/${x}';
        } else {
            x = 'C' + this.zeroPad(x, 8, 16);
            y = 'R' + this.zeroPad(y, 8, 16);
            z = 'L' + this.zeroPad(z, 2, 16);
            url = url + '/${z}/${y}/${x}.' + this.type;
        }
        url = OpenLayers.String.format(url, {
            'x': x,
            'y': y,
            'z': z
        });
        return url;
    },
    zeroPad: function (num, len, radix) {
        var str = num.toString(radix || 10);
        while (str.length < len) {
            str = "0" + str;
        }
        return str;
    },
    CLASS_NAME: 'OpenLayers.Layer.ArcGISCache'
});
OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, {
    hover: false,
    drillDown: false,
    maxFeatures: 10,
    clickCallback: "click",
    output: "features",
    layers: null,
    queryVisible: false,
    url: null,
    layerUrls: null,
    infoFormat: 'text/html',
    vendorParams: {},
    format: null,
    formatOptions: null,
    handlerOptions: null,
    handler: null,
    hoverRequest: null,
    EVENT_TYPES: ["beforegetfeatureinfo", "nogetfeatureinfo", "getfeatureinfo"],
    initialize: function (options) {
        this.EVENT_TYPES = OpenLayers.Control.WMSGetFeatureInfo.prototype.EVENT_TYPES.concat(OpenLayers.Control.prototype.EVENT_TYPES);
        options = options || {};
        options.handlerOptions = options.handlerOptions || {};
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        if (!this.format) {
            this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions);
        }
        if (this.drillDown === true) {
            this.hover = false;
        }
        if (this.hover) {
            this.handler = new OpenLayers.Handler.Hover(this, {
                'move': this.cancelHover,
                'pause': this.getInfoForHover
            }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, {
                'delay': 250
            }));
        } else {
            var callbacks = {};
            callbacks[this.clickCallback] = this.getInfoForClick;
            this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {});
        }
    },
    activate: function () {
        if (!this.active) {
            this.handler.activate();
        }
        return OpenLayers.Control.prototype.activate.apply(this, arguments);
    },
    deactivate: function () {
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
    },
    getInfoForClick: function (evt) {
        this.events.triggerEvent("beforegetfeatureinfo", {
            xy: evt.xy
        });
        OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait");
        this.request(evt.xy, {});
    },
    getInfoForHover: function (evt) {
        this.events.triggerEvent("beforegetfeatureinfo", {
            xy: evt.xy
        });
        this.request(evt.xy, {
            hover: true
        });
    },
    cancelHover: function () {
        if (this.hoverRequest) {
            this.hoverRequest.abort();
            this.hoverRequest = null;
        }
    },
    findLayers: function () {
        var candidates = this.layers || this.map.layers;
        var layers = [];
        var layer, url;
        for (var i = 0, len = candidates.length; i < len; ++i) {
            layer = candidates[i];
            if (layer instanceof OpenLayers.Layer.WMS && (!this.queryVisible || layer.getVisibility())) {
                url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
                if (this.drillDown === false && !this.url) {
                    this.url = url;
                }
                if (this.drillDown === true || this.urlMatches(url)) {
                    layers.push(layer);
                }
            }
        }
        return layers;
    },
    urlMatches: function (url) {
        var matches = OpenLayers.Util.isEquivalentUrl(this.url, url);
        if (!matches && this.layerUrls) {
            for (var i = 0, len = this.layerUrls.length; i < len; ++i) {
                if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) {
                    matches = true;
                    break;
                }
            }
        }
        return matches;
    },
    buildWMSOptions: function (url, layers, clickPosition, format) {
        var layerNames = [],
            styleNames = [];
        for (var i = 0, len = layers.length; i < len; i++) {
            layerNames = layerNames.concat(layers[i].params.LAYERS);
            styleNames = styleNames.concat(this.getStyleNames(layers[i]));
        }
        var firstLayer = layers[0];
        var projection = this.map.getProjection();
        var layerProj = firstLayer.projection;
        if (layerProj && layerProj.equals(this.map.getProjectionObject())) {
            projection = layerProj.getCode();
        }
        var params = OpenLayers.Util.extend({
            service: "WMS",
            version: firstLayer.params.VERSION,
            request: "GetFeatureInfo",
            layers: layerNames,
            query_layers: layerNames,
            styles: styleNames,
            bbox: this.map.getExtent().toBBOX(null, firstLayer.reverseAxisOrder()),
            feature_count: this.maxFeatures,
            height: this.map.getSize().h,
            width: this.map.getSize().w,
            format: format,
            info_format: firstLayer.params.INFO_FORMAT || this.infoFormat
        }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? {
            crs: projection,
            i: parseInt(clickPosition.x),
            j: parseInt(clickPosition.y)
        } : {
            srs: projection,
            x: parseInt(clickPosition.x),
            y: parseInt(clickPosition.y)
        });
        OpenLayers.Util.applyDefaults(params, this.vendorParams);
        return {
            url: url,
            params: OpenLayers.Util.upperCaseObject(params),
            callback: function (request) {
                this.handleResponse(clickPosition, request, url);
            },
            scope: this
        };
    },
    getStyleNames: function (layer) {
        var styleNames;
        if (layer.params.STYLES) {
            styleNames = layer.params.STYLES;
        } else {
            if (OpenLayers.Util.isArray(layer.params.LAYERS)) {
                styleNames = new Array(layer.params.LAYERS.length);
            } else {
                styleNames = layer.params.LAYERS.replace(/[^,]/g, "");
            }
        }
        return styleNames;
    },
    request: function (clickPosition, options) {
        var layers = this.findLayers();
        if (layers.length == 0) {
            this.events.triggerEvent("nogetfeatureinfo");
            OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
            return;
        }
        options = options || {};
        if (this.drillDown === false) {
            var wmsOptions = this.buildWMSOptions(this.url, layers, clickPosition, layers[0].params.FORMAT);
            var request = OpenLayers.Request.GET(wmsOptions);
            if (options.hover === true) {
                this.hoverRequest = request;
            }
        } else {
            this._requestCount = 0;
            this._numRequests = 0;
            this.features = [];
            var services = {},
                url;
            for (var i = 0, len = layers.length; i < len; i++) {
                var layer = layers[i];
                var service, found = false;
                url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url;
                if (url in services) {
                    services[url].push(layer);
                } else {
                    this._numRequests++;
                    services[url] = [layer];
                }
            }
            var layers;
            for (var url in services) {
                layers = services[url];
                var wmsOptions = this.buildWMSOptions(url, layers, clickPosition, layers[0].params.FORMAT);
                OpenLayers.Request.GET(wmsOptions);
            }
        }
    },
    triggerGetFeatureInfo: function (request, xy, features) {
        this.events.triggerEvent("getfeatureinfo", {
            text: request.responseText,
            features: features,
            request: request,
            xy: xy
        });
        OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait");
    },
    handleResponse: function (xy, request, url) {
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        var features = this.format.read(doc);
        if (this.drillDown === false) {
            this.triggerGetFeatureInfo(request, xy, features);
        } else {
            this._requestCount++;
            if (this.output === "object") {
                this._features = (this._features || []).concat({
                    url: url,
                    features: features
                });
            } else {
                this._features = (this._features || []).concat(features);
            }
            if (this._requestCount === this._numRequests) {
                this.triggerGetFeatureInfo(request, xy, this._features.concat());
                delete this._features;
                delete this._requestCount;
                delete this._numRequests;
            }
        }
    },
    CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo"
});
OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_3, {
    version: "1.3.0",
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3_0"
});
OpenLayers.Format.CQL = (function () {
    var tokens = ["PROPERTY", "COMPARISON", "VALUE", "LOGICAL"],
        patterns = {
            PROPERTY: /^[_a-zA-Z]\w*/,
            COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i,
            COMMA: /^,/,
            LOGICAL: /^(AND|OR)/i,
            VALUE: /^('\w+'|\d+(\.\d*)?|\.\d+)/,
            LPAREN: /^\(/,
            RPAREN: /^\)/,
            SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i,
            NOT: /^NOT/i,
            BETWEEN: /^BETWEEN/i,
            GEOMETRY: function (text) {
                var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text);
                if (type) {
                    var len = text.length;
                    var idx = text.indexOf("(", type[0].length);
                    if (idx > -1) {
                        var depth = 1;
                        while (idx < len && depth > 0) {
                            idx++;
                            switch (text.charAt(idx)) {
                            case '(':
                                depth++;
                                break;
                            case ')':
                                depth--;
                                break;
                            default:
                            }
                        }
                    }
                    return [text.substr(0, idx + 1)];
                }
            },
            END: /^$/
        },
        follows = {
            LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'],
            RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'],
            PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA'],
            BETWEEN: ['VALUE'],
            COMPARISON: ['VALUE'],
            COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'],
            VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'],
            SPATIAL: ['LPAREN'],
            LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'],
            NOT: ['PROPERTY', 'LPAREN'],
            GEOMETRY: ['COMMA', 'RPAREN']
        },
        operators = {
            '=': OpenLayers.Filter.Comparison.EQUAL_TO,
            '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
            '<': OpenLayers.Filter.Comparison.LESS_THAN,
            '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
            '>': OpenLayers.Filter.Comparison.GREATER_THAN,
            '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
            'LIKE': OpenLayers.Filter.Comparison.LIKE,
            'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN
        },
        operatorReverse = {},
        logicals = {
            'AND': OpenLayers.Filter.Logical.AND,
            'OR': OpenLayers.Filter.Logical.OR
        },
        logicalReverse = {},
        precedence = {
            'RPAREN': 3,
            'LOGICAL': 2,
            'COMPARISON': 1
        };
    var i;
    for (i in operators) {
        if (operators.hasOwnProperty(i)) {
            operatorReverse[operators[i]] = i;
        }
    }
    for (i in logicals) {
        if (logicals.hasOwnProperty(i)) {
            logicalReverse[logicals[i]] = i;
        }
    }

    function tryToken(text, pattern) {
        if (pattern instanceof RegExp) {
            return pattern.exec(text);
        } else {
            return pattern(text);
        }
    }

    function nextToken(text, tokens) {
        var i, token, len = tokens.length;
        for (i = 0; i < len; i++) {
            token = tokens[i];
            var pat = patterns[token];
            var matches = tryToken(text, pat);
            if (matches) {
                var match = matches[0];
                var remainder = text.substr(match.length).replace(/^\s*/, "");
                return {
                    type: token,
                    text: match,
                    remainder: remainder
                };
            }
        }
        var msg = "ERROR: In parsing: [" + text + "], expected one of: ";
        for (i = 0; i < len; i++) {
            token = tokens[i];
            msg += "\n    " + token + ": " + patterns[token];
        }
        throw new Error(msg);
    }

    function tokenize(text) {
        var results = [];
        var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"];
        do {
            token = nextToken(text, expect);
            text = token.remainder;
            expect = follows[token.type];
            if (token.type != "END" && !expect) {
                throw new Error("No follows list for " + token.type);
            }
            results.push(token);
        } while (token.type != "END");
        return results;
    }

    function buildAst(tokens) {
        var operatorStack = [],
            postfix = [];
        while (tokens.length) {
            var tok = tokens.shift();
            switch (tok.type) {
            case "PROPERTY":
            case "GEOMETRY":
            case "VALUE":
                postfix.push(tok);
                break;
            case "COMPARISON":
            case "BETWEEN":
            case "LOGICAL":
                var p = precedence[tok.type];
                while (operatorStack.length > 0 && (precedence[operatorStack[operatorStack.length - 1].type] <= p)) {
                    postfix.push(operatorStack.pop());
                }
                operatorStack.push(tok);
                break;
            case "SPATIAL":
            case "NOT":
            case "LPAREN":
                operatorStack.push(tok);
                break;
            case "RPAREN":
                while (operatorStack.length > 0 && (operatorStack[operatorStack.length - 1].type != "LPAREN")) {
                    postfix.push(operatorStack.pop());
                }
                operatorStack.pop();
                if (operatorStack.length > 0 && operatorStack[operatorStack.length - 1].type == "SPATIAL") {
                    postfix.push(operatorStack.pop());
                }
            case "COMMA":
            case "END":
                break;
            default:
                throw new Error("Unknown token type " + tok.type);
            }
        }
        while (operatorStack.length > 0) {
            postfix.push(operatorStack.pop());
        }

        function buildTree() {
            var tok = postfix.pop();
            switch (tok.type) {
            case "LOGICAL":
                var rhs = buildTree(),
                    lhs = buildTree();
                return new OpenLayers.Filter.Logical({
                    filters: [lhs, rhs],
                    type: logicals[tok.text.toUpperCase()]
                });
            case "NOT":
                var operand = buildTree();
                return new OpenLayers.Filter.Logical({
                    filters: [operand],
                    type: OpenLayers.Filter.Logical.NOT
                });
            case "BETWEEN":
                var min, max, property;
                postfix.pop();
                max = buildTree();
                min = buildTree();
                property = buildTree();
                return new OpenLayers.Filter.Comparison({
                    property: property,
                    lowerBoundary: min,
                    upperBoundary: max,
                    type: OpenLayers.Filter.Comparison.BETWEEN
                });
            case "COMPARISON":
                var value = buildTree(),
                    property = buildTree();
                return new OpenLayers.Filter.Comparison({
                    property: property,
                    value: value,
                    type: operators[tok.text.toUpperCase()]
                });
            case "VALUE":
                if ((/^'.*'$/).test(tok.text)) {
                    return tok.text.substr(1, tok.text.length - 2);
                } else {
                    return Number(tok.text);
                }
            case "SPATIAL":
                switch (tok.text.toUpperCase()) {
                case "BBOX":
                    var maxy = buildTree(),
                        maxx = buildTree(),
                        miny = buildTree(),
                        minx = buildTree(),
                        prop = buildTree();
                    return new OpenLayers.Filter.Spatial({
                        type: OpenLayers.Filter.Spatial.BBOX,
                        property: prop,
                        value: OpenLayers.Bounds.fromArray([minx, miny, maxx, maxy])
                    });
                case "INTERSECTS":
                    var value = buildTree(),
                        property = buildTree();
                    return new OpenLayers.Filter.Spatial({
                        type: OpenLayers.Filter.Spatial.INTERSECTS,
                        property: property,
                        value: value
                    });
                case "WITHIN":
                    var value = buildTree(),
                        property = buildTree();
                    return new OpenLayers.Filter.Spatial({
                        type: OpenLayers.Filter.Spatial.WITHIN,
                        property: property,
                        value: value
                    });
                case "CONTAINS":
                    var value = buildTree(),
                        property = buildTree();
                    return new OpenLayers.Filter.Spatial({
                        type: OpenLayers.Filter.Spatial.CONTAINS,
                        property: property,
                        value: value
                    });
                case "DWITHIN":
                    var distance = buildTree(),
                        value = buildTree(),
                        property = buildTree();
                    return new OpenLayers.Filter.Spatial({
                        type: OpenLayers.Filter.Spatial.DWITHIN,
                        value: value,
                        property: property,
                        distance: Number(distance)
                    });
                }
            case "GEOMETRY":
                return OpenLayers.Geometry.fromWKT(tok.text);
            default:
                return tok.text;
            }
        }
        var result = buildTree();
        if (postfix.length > 0) {
            var msg = "Remaining tokens after building AST: \n";
            for (var i = postfix.length - 1; i >= 0; i--) {
                msg += postfix[i].type + ": " + postfix[i].text + "\n";
            }
            throw new Error(msg);
        }
        return result;
    }
    return OpenLayers.Class(OpenLayers.Format, {
        read: function (text) {
            var result = buildAst(tokenize(text));
            if (this.keepData) {
                this.data = result;
            };
            return result;
        },
        write: function (filter) {
            if (filter instanceof OpenLayers.Geometry) {
                return filter.toString();
            }
            switch (filter.CLASS_NAME) {
            case "OpenLayers.Filter.Spatial":
                switch (filter.type) {
                case OpenLayers.Filter.Spatial.BBOX:
                    return "BBOX(" + filter.property + "," + filter.value.toBBOX() + ")";
                case OpenLayers.Filter.Spatial.DWITHIN:
                    return "DWITHIN(" + filter.property + ", " + this.write(filter.value) + ", " + filter.distance + ")";
                case OpenLayers.Filter.Spatial.WITHIN:
                    return "WITHIN(" + filter.property + ", " + this.write(filter.value) + ")";
                case OpenLayers.Filter.Spatial.INTERSECTS:
                    return "INTERSECTS(" + filter.property + ", " + this.write(filter.value) + ")";
                case OpenLayers.Filter.Spatial.CONTAINS:
                    return "CONTAINS(" + filter.property + ", " + this.write(filter.value) + ")";
                default:
                    throw new Error("Unknown spatial filter type: " + filter.type);
                }
            case "OpenLayers.Filter.Logical":
                if (filter.type == OpenLayers.Filter.Logical.NOT) {
                    return "NOT (" + this.write(filter.filters[0]) + ")";
                } else {
                    var res = "(";
                    var first = true;
                    for (var i = 0; i < filter.filters.length; i++) {
                        if (first) {
                            first = false;
                        } else {
                            res += ") " + logicalReverse[filter.type] + " (";
                        }
                        res += this.write(filter.filters[i]);
                    }
                    return res + ")";
                }
            case "OpenLayers.Filter.Comparison":
                if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) {
                    return filter.property + " BETWEEN " + this.write(filter.lowerBoundary) + " AND " + this.write(filter.upperBoundary);
                } else {
                    return filter.property + " " + operatorReverse[filter.type] + " " + this.write(filter.value);
                }
            case undefined:
                if (typeof filter === "string") {
                    return "'" + filter + "'";
                } else if (typeof filter === "number") {
                    return String(filter);
                }
            default:
                throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter);
            }
        },
        CLASS_NAME: "OpenLayers.Format.CQL"
    });
})();
OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, {
    EVENT_TYPES: ["beforesplit", "split", "aftersplit"],
    layer: null,
    source: null,
    sourceOptions: null,
    tolerance: null,
    edge: true,
    deferDelete: false,
    mutual: true,
    targetFilter: null,
    sourceFilter: null,
    handler: null,
    initialize: function (options) {
        Array.prototype.push.apply(this.EVENT_TYPES, OpenLayers.Control.prototype.EVENT_TYPES);
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.options = options || {};
        if (this.options.source) {
            this.setSource(this.options.source);
        }
    },
    setSource: function (layer) {
        if (this.active) {
            this.deactivate();
            if (this.handler) {
                this.handler.destroy();
                delete this.handler;
            }
            this.source = layer;
            this.activate();
        } else {
            this.source = layer;
        }
    },
    activate: function () {
        var activated = OpenLayers.Control.prototype.activate.call(this);
        if (activated) {
            if (!this.source) {
                if (!this.handler) {
                    this.handler = new OpenLayers.Handler.Path(this, {
                        done: function (geometry) {
                            this.onSketchComplete({
                                feature: new OpenLayers.Feature.Vector(geometry)
                            });
                        }
                    }, {
                        layerOptions: this.sourceOptions
                    });
                }
                this.handler.activate();
            } else if (this.source.events) {
                this.source.events.on({
                    sketchcomplete: this.onSketchComplete,
                    afterfeaturemodified: this.afterFeatureModified,
                    scope: this
                });
            }
        }
        return activated;
    },
    deactivate: function () {
        var deactivated = OpenLayers.Control.prototype.deactivate.call(this);
        if (deactivated) {
            if (this.source && this.source.events) {
                this.layer.events.un({
                    sketchcomplete: this.onSketchComplete,
                    afterfeaturemodified: this.afterFeatureModified,
                    scope: this
                });
            }
        }
        return deactivated;
    },
    onSketchComplete: function (event) {
        this.feature = null;
        return !this.considerSplit(event.feature);
    },
    afterFeatureModified: function (event) {
        if (event.modified) {
            var feature = event.feature;
            if (feature.geometry instanceof OpenLayers.Geometry.LineString || feature.geometry instanceof OpenLayers.Geometry.MultiLineString) {
                this.feature = event.feature;
                this.considerSplit(event.feature);
            }
        }
    },
    removeByGeometry: function (features, geometry) {
        for (var i = 0, len = features.length; i < len; ++i) {
            if (features[i].geometry === geometry) {
                features.splice(i, 1);
                break;
            }
        }
    },
    isEligible: function (target) {
        return (target.state !== OpenLayers.State.DELETE) && (target.geometry instanceof OpenLayers.Geometry.LineString || target.geometry instanceof OpenLayers.Geometry.MultiLineString) && (this.feature !== target) && (!this.targetFilter || this.targetFilter.evaluate(target.attributes));
    },
    considerSplit: function (feature) {
        var sourceSplit = false;
        var targetSplit = false;
        if (!this.sourceFilter || this.sourceFilter.evaluate(feature.attributes)) {
            var features = this.layer && this.layer.features || [];
            var target, results, proceed;
            var additions = [],
                removals = [];
            var mutual = (this.layer === this.source) && this.mutual;
            var options = {
                edge: this.edge,
                tolerance: this.tolerance,
                mutual: mutual
            };
            var sourceParts = [feature.geometry];
            var targetFeature, targetParts;
            var source, parts;
            for (var i = 0, len = features.length; i < len; ++i) {
                targetFeature = features[i];
                if (this.isEligible(targetFeature)) {
                    targetParts = [targetFeature.geometry];
                    for (var j = 0; j < sourceParts.length; ++j) {
                        source = sourceParts[j];
                        for (var k = 0; k < targetParts.length; ++k) {
                            target = targetParts[k];
                            if (source.getBounds().intersectsBounds(target.getBounds())) {
                                results = source.split(target, options);
                                if (results) {
                                    proceed = this.events.triggerEvent("beforesplit", {
                                        source: feature,
                                        target: targetFeature
                                    });
                                    if (proceed !== false) {
                                        if (mutual) {
                                            parts = results[0];
                                            if (parts.length > 1) {
                                                parts.unshift(j, 1);
                                                Array.prototype.splice.apply(sourceParts, parts);
                                                j += parts.length - 3;
                                            }
                                            results = results[1];
                                        }
                                        if (results.length > 1) {
                                            results.unshift(k, 1);
                                            Array.prototype.splice.apply(targetParts, results);
                                            k += results.length - 3;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    if (targetParts && targetParts.length > 1) {
                        this.geomsToFeatures(targetFeature, targetParts);
                        this.events.triggerEvent("split", {
                            original: targetFeature,
                            features: targetParts
                        });
                        Array.prototype.push.apply(additions, targetParts);
                        removals.push(targetFeature);
                        targetSplit = true;
                    }
                }
            }
            if (sourceParts && sourceParts.length > 1) {
                this.geomsToFeatures(feature, sourceParts);
                this.events.triggerEvent("split", {
                    original: feature,
                    features: sourceParts
                });
                Array.prototype.push.apply(additions, sourceParts);
                removals.push(feature);
                sourceSplit = true;
            }
            if (sourceSplit || targetSplit) {
                if (this.deferDelete) {
                    var feat, destroys = [];
                    for (var i = 0, len = removals.length; i < len; ++i) {
                        feat = removals[i];
                        if (feat.state === OpenLayers.State.INSERT) {
                            destroys.push(feat);
                        } else {
                            feat.state = OpenLayers.State.DELETE;
                            this.layer.drawFeature(feat);
                        }
                    }
                    this.layer.destroyFeatures(destroys, {
                        silent: true
                    });
                    for (var i = 0, len = additions.length; i < len; ++i) {
                        additions[i].state = OpenLayers.State.INSERT;
                    }
                } else {
                    this.layer.destroyFeatures(removals, {
                        silent: true
                    });
                }
                this.layer.addFeatures(additions, {
                    silent: true
                });
                this.events.triggerEvent("aftersplit", {
                    source: feature,
                    features: additions
                });
            }
        }
        return sourceSplit;
    },
    geomsToFeatures: function (feature, geoms) {
        var clone = feature.clone();
        delete clone.geometry;
        var newFeature;
        for (var i = 0, len = geoms.length; i < len; ++i) {
            newFeature = clone.clone();
            newFeature.geometry = geoms[i];
            newFeature.state = OpenLayers.State.INSERT;
            geoms[i] = newFeature;
        }
    },
    destroy: function () {
        if (this.active) {
            this.deactivate();
        }
        OpenLayers.Control.prototype.destroy.call(this);
    },
    CLASS_NAME: "OpenLayers.Control.Split"
});
OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
    isBaseLayer: true,
    version: "1.0.0",
    requestEncoding: "KVP",
    url: null,
    layer: null,
    matrixSet: null,
    style: null,
    format: "image/jpeg",
    tileOrigin: null,
    tileFullExtent: null,
    formatSuffix: null,
    matrixIds: null,
    dimensions: null,
    params: null,
    zoomOffset: 0,
    formatSuffixMap: {
        "image/png": "png",
        "image/png8": "png",
        "image/png24": "png",
        "image/png32": "png",
        "png": "png",
        "image/jpeg": "jpg",
        "image/jpg": "jpg",
        "jpeg": "jpg",
        "jpg": "jpg"
    },
    matrix: null,
    initialize: function (config) {
        var required = {
            url: true,
            layer: true,
            style: true,
            matrixSet: true
        };
        for (var prop in required) {
            if (!(prop in config)) {
                throw new Error("Missing property '" + prop + "' in layer configuration.");
            }
        }
        config.params = OpenLayers.Util.upperCaseObject(config.params);
        var args = [config.name, config.url, config.params, config];
        OpenLayers.Layer.Grid.prototype.initialize.apply(this, args);
        if (!this.formatSuffix) {
            this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop();
        }
        if (this.matrixIds) {
            var len = this.matrixIds.length;
            if (len && typeof this.matrixIds[0] === "string") {
                var ids = this.matrixIds;
                this.matrixIds = new Array(len);
                for (var i = 0; i < len; ++i) {
                    this.matrixIds[i] = {
                        identifier: ids[i]
                    };
                }
            }
        }
    },
    setMap: function () {
        OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments);
        this.updateMatrixProperties();
    },
    updateMatrixProperties: function () {
        this.matrix = this.getMatrix();
        if (this.matrix) {
            if (this.matrix.topLeftCorner) {
                this.tileOrigin = this.matrix.topLeftCorner;
            }
            if (this.matrix.tileWidth && this.matrix.tileHeight) {
                this.tileSize = new OpenLayers.Size(this.matrix.tileWidth, this.matrix.tileHeight);
            }
            if (!this.tileOrigin) {
                this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top);
            }
            if (!this.tileFullExtent) {
                this.tileFullExtent = this.maxExtent;
            }
        }
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        if (zoomChanged || !this.matrix) {
            this.updateMatrixProperties();
        }
        return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WMTS(this.options);
        }
        obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]);
        return obj;
    },
    getMatrix: function () {
        var matrix;
        if (!this.matrixIds || this.matrixIds.length === 0) {
            matrix = {
                identifier: this.map.getZoom() + this.zoomOffset
            };
        } else {
            if ("scaleDenominator" in this.matrixIds[0]) {
                var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * this.map.getResolution() / 0.28E-3;
                var diff = Number.POSITIVE_INFINITY;
                var delta;
                for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) {
                    delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom));
                    if (delta < diff) {
                        diff = delta;
                        matrix = this.matrixIds[i];
                    }
                }
            } else {
                matrix = this.matrixIds[this.map.getZoom() + this.zoomOffset];
            }
        }
        return matrix;
    },
    getTileInfo: function (loc) {
        var res = this.map.getResolution();
        var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
        var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);
        var col = Math.floor(fx);
        var row = Math.floor(fy);
        return {
            col: col,
            row: row,
            i: Math.floor((fx - col) * this.tileSize.w),
            j: Math.floor((fy - row) * this.tileSize.h)
        };
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var url = "";
        if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) {
            var center = bounds.getCenterLonLat();
            var info = this.getTileInfo(center);
            var matrixId = this.matrix.identifier;
            if (this.requestEncoding.toUpperCase() === "REST") {
                var path = this.version + "/" + this.layer + "/" + this.style + "/";
                if (this.dimensions) {
                    for (var i = 0; i < this.dimensions.length; i++) {
                        if (this.params[this.dimensions[i]]) {
                            path = path + this.params[this.dimensions[i]] + "/";
                        }
                    }
                }
                path = path + this.matrixSet + "/" + this.matrix.identifier + "/" + info.row + "/" + info.col + "." + this.formatSuffix;
                if (OpenLayers.Util.isArray(this.url)) {
                    url = this.selectUrl(path, this.url);
                } else {
                    url = this.url;
                }
                if (!url.match(/\/$/)) {
                    url = url + "/";
                }
                url = url + path;
            } else if (this.requestEncoding.toUpperCase() === "KVP") {
                var params = {
                    SERVICE: "WMTS",
                    REQUEST: "GetTile",
                    VERSION: this.version,
                    LAYER: this.layer,
                    STYLE: this.style,
                    TILEMATRIXSET: this.matrixSet,
                    TILEMATRIX: this.matrix.identifier,
                    TILEROW: info.row,
                    TILECOL: info.col,
                    FORMAT: this.format
                };
                url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]);
            }
        }
        return url;
    },
    mergeNewParams: function (newParams) {
        if (this.requestEncoding.toUpperCase() === "KVP") {
            return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, [OpenLayers.Util.upperCaseObject(newParams)]);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.WMTS"
});
OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, {
    fois: null,
    formatOptions: null,
    initialize: function (options) {
        OpenLayers.Protocol.prototype.initialize.apply(this, [options]);
        if (!options.format) {
            this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(this.formatOptions);
        }
    },
    destroy: function () {
        if (this.options && !this.options.format) {
            this.format.destroy();
        }
        this.format = null;
        OpenLayers.Protocol.prototype.destroy.apply(this);
    },
    read: function (options) {
        options = OpenLayers.Util.extend({}, options);
        OpenLayers.Util.applyDefaults(options, this.options || {});
        var response = new OpenLayers.Protocol.Response({
            requestType: "read"
        });
        var format = this.format;
        var data = OpenLayers.Format.XML.prototype.write.apply(format, [format.writeNode("sos:GetFeatureOfInterest", {
            fois: this.fois
        })]);
        response.priv = OpenLayers.Request.POST({
            url: options.url,
            callback: this.createCallback(this.handleRead, response, options),
            data: data
        });
        return response;
    },
    handleRead: function (response, options) {
        if (options.callback) {
            var request = response.priv;
            if (request.status >= 200 && request.status < 300) {
                response.features = this.parseFeatures(request);
                response.code = OpenLayers.Protocol.Response.SUCCESS;
            } else {
                response.code = OpenLayers.Protocol.Response.FAILURE;
            }
            options.callback.call(options.scope, response);
        }
    },
    parseFeatures: function (request) {
        var doc = request.responseXML;
        if (!doc || !doc.documentElement) {
            doc = request.responseText;
        }
        if (!doc || doc.length <= 0) {
            return null;
        }
        return this.format.read(doc);
    },
    CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0"
});
OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, {
    IMAGE_EXTENSIONS: {
        'jpeg': 'jpg',
        'gif': 'gif',
        'png': 'png',
        'png8': 'png',
        'png24': 'png',
        'dithered': 'png'
    },
    DEFAULT_FORMAT: 'jpeg',
    initialize: function (name, url, params, options) {
        OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments);
        this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || DEFAULT_FORMAT];
    },
    getURL: function (bounds) {
        bounds = this.adjustBounds(bounds);
        var mapRes = this.map.getResolution();
        var scale = Math.round((this.map.getScale() * 10000)) / 10000;
        var pX = Math.round(bounds.left / mapRes);
        var pY = -Math.round(bounds.top / mapRes);
        var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w;
        var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h;
        var url = this.url;
        if (OpenLayers.Util.isArray(url)) {
            url = this.selectUrl(paramsString, url);
        }
        var components = [url, "/", this.params.map, "/", scale, "/", this.params.g.replace(/\s/g, '_'), "/def/t", metaY, "/l", metaX, "/t", pY, "l", pX, ".", this.extension];
        return components.join("");
    },
    CLASS_NAME: "OpenLayers.Layer.KaMapCache"
});
OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, {
    version: "1.1.0",
    initialize: function (options) {
        OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments);
        if (this.outputFormat && !this.readFormat) {
            if (this.outputFormat.toLowerCase() == "gml2") {
                this.readFormat = new OpenLayers.Format.GML.v2({
                    featureType: this.featureType,
                    featureNS: this.featureNS,
                    geometryName: this.geometryName
                });
            } else if (this.outputFormat.toLowerCase() == "json") {
                this.readFormat = new OpenLayers.Format.GeoJSON();
            }
        }
    },
    CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0"
});
OpenLayers.Format.WMSCapabilities.v1_1_1 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1, {
    version: "1.1.1",
    initialize: function (options) {
        OpenLayers.Format.WMSCapabilities.v1_1.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "wms": OpenLayers.Util.applyDefaults({
            "SRS": function (node, obj) {
                obj.srs[this.getChildValue(node)] = true;
            }
        }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"])
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1"
});
OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1_1, {
    version: "1.1.1",
    profile: "WMSC",
    initialize: function (options) {
        OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "wms": OpenLayers.Util.applyDefaults({
            "VendorSpecificCapabilities": function (node, obj) {
                obj.vendorSpecific = {
                    tileSets: []
                };
                this.readChildNodes(node, obj.vendorSpecific);
            },
            "TileSet": function (node, vendorSpecific) {
                var tileset = {
                    srs: {},
                    bbox: {},
                    resolutions: []
                };
                this.readChildNodes(node, tileset);
                vendorSpecific.tileSets.push(tileset);
            },
            "Resolutions": function (node, tileset) {
                var res = this.getChildValue(node).split(" ");
                for (var i = 0, len = res.length; i < len; i++) {
                    if (res[i] != "") {
                        tileset.resolutions.push(parseFloat(res[i]));
                    }
                }
            },
            "Width": function (node, tileset) {
                tileset.width = parseInt(this.getChildValue(node));
            },
            "Height": function (node, tileset) {
                tileset.height = parseInt(this.getChildValue(node));
            },
            "Layers": function (node, tileset) {
                tileset.layers = this.getChildValue(node);
            },
            "Styles": function (node, tileset) {
                tileset.styles = this.getChildValue(node);
            }
        }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers["wms"])
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC"
});
OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WMSCapabilities.v1_1, {
    version: "1.1.0",
    initialize: function (options) {
        OpenLayers.Format.WMSCapabilities.v1_1.prototype.initialize.apply(this, [options]);
    },
    readers: {
        "wms": OpenLayers.Util.applyDefaults({
            "SRS": function (node, obj) {
                var srs = this.getChildValue(node);
                var values = srs.split(/ +/);
                for (var i = 0, len = values.length; i < len; i++) {
                    obj.srs[values[i]] = true;
                }
            }
        }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"])
    },
    CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_0"
});
OpenLayers.Layer.WFS = OpenLayers.Class(OpenLayers.Layer.Vector, OpenLayers.Layer.Markers, {
    isBaseLayer: false,
    tile: null,
    ratio: 2,
    DEFAULT_PARAMS: {
        service: "WFS",
        version: "1.0.0",
        request: "GetFeature"
    },
    featureClass: null,
    format: null,
    formatObject: null,
    formatOptions: null,
    vectorMode: true,
    encodeBBOX: false,
    extractAttributes: false,
    initialize: function (name, url, params, options) {
        if (options == undefined) {
            options = {};
        }
        if (options.featureClass || !OpenLayers.Layer.Vector || !OpenLayers.Feature.Vector) {
            this.vectorMode = false;
        }
        params = OpenLayers.Util.upperCaseObject(params);
        OpenLayers.Util.extend(options, {
            'reportError': false
        });
        var newArguments = [];
        newArguments.push(name, options);
        OpenLayers.Layer.Vector.prototype.initialize.apply(this, newArguments);
        if (!this.renderer || !this.vectorMode) {
            this.vectorMode = false;
            if (!options.featureClass) {
                options.featureClass = OpenLayers.Feature.WFS;
            }
            OpenLayers.Layer.Markers.prototype.initialize.apply(this, newArguments);
        }
        if (this.params && this.params.typename && !this.options.typename) {
            this.options.typename = this.params.typename;
        }
        if (!this.options.geometry_column) {
            this.options.geometry_column = "the_geom";
        }
        this.params = OpenLayers.Util.applyDefaults(params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS));
        this.url = url;
    },
    destroy: function () {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.destroy.apply(this, arguments);
        } else {
            OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments);
        }
        if (this.tile) {
            this.tile.destroy();
        }
        this.tile = null;
        this.ratio = null;
        this.featureClass = null;
        this.format = null;
        if (this.formatObject && this.formatObject.destroy) {
            this.formatObject.destroy();
        }
        this.formatObject = null;
        this.formatOptions = null;
        this.vectorMode = null;
        this.encodeBBOX = null;
        this.extractAttributes = null;
    },
    setMap: function (map) {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments);
            var options = {
                'extractAttributes': this.extractAttributes
            };
            OpenLayers.Util.extend(options, this.formatOptions);
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
                options.externalProjection = this.projection;
                options.internalProjection = this.map.getProjectionObject();
            }
            this.formatObject = this.format ? new this.format(options) : new OpenLayers.Format.GML(options);
        } else {
            OpenLayers.Layer.Markers.prototype.setMap.apply(this, arguments);
        }
    },
    moveTo: function (bounds, zoomChanged, dragging) {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.moveTo.apply(this, arguments);
        } else {
            OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments);
        }
        if (dragging) {
            return false;
        }
        if (zoomChanged) {
            if (this.vectorMode) {
                this.renderer.clear();
            }
        }
        if (this.options.minZoomLevel) {
            OpenLayers.Console.warn(OpenLayers.i18n('minZoomLevelError'));
            if (this.map.getZoom() < this.options.minZoomLevel) {
                return null;
            }
        }
        if (bounds == null) {
            bounds = this.map.getExtent();
        }
        var firstRendering = (this.tile == null);
        var outOfBounds = (!firstRendering && !this.tile.bounds.containsBounds(bounds));
        if (zoomChanged || firstRendering || (!dragging && outOfBounds)) {
            var center = bounds.getCenterLonLat();
            var tileWidth = bounds.getWidth() * this.ratio;
            var tileHeight = bounds.getHeight() * this.ratio;
            var tileBounds = new OpenLayers.Bounds(center.lon - (tileWidth / 2), center.lat - (tileHeight / 2), center.lon + (tileWidth / 2), center.lat + (tileHeight / 2));
            var tileSize = this.map.getSize();
            tileSize.w = tileSize.w * this.ratio;
            tileSize.h = tileSize.h * this.ratio;
            var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top);
            var pos = this.map.getLayerPxFromLonLat(ul);
            var url = this.getFullRequestString();
            var params = null;
            var filter = this.params.filter || this.params.FILTER;
            if (filter) {
                params = {
                    FILTER: filter
                };
            } else {
                params = {
                    BBOX: this.encodeBBOX ? tileBounds.toBBOX() : tileBounds.toArray()
                };
            }
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
                var projectedBounds = tileBounds.clone();
                projectedBounds.transform(this.map.getProjectionObject(), this.projection);
                if (!filter) {
                    params.BBOX = this.encodeBBOX ? projectedBounds.toBBOX() : projectedBounds.toArray();
                }
            }
            url += "&" + OpenLayers.Util.getParameterString(params);
            if (!this.tile) {
                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds, url, tileSize);
                this.addTileMonitoringHooks(this.tile);
                this.tile.draw();
            } else {
                if (this.vectorMode) {
                    this.destroyFeatures();
                    this.renderer.clear();
                } else {
                    this.clearMarkers();
                }
                this.removeTileMonitoringHooks(this.tile);
                this.tile.destroy();
                this.tile = null;
                this.tile = new OpenLayers.Tile.WFS(this, pos, tileBounds, url, tileSize);
                this.addTileMonitoringHooks(this.tile);
                this.tile.draw();
            }
        }
    },
    addTileMonitoringHooks: function (tile) {
        tile.onLoadStart = function () {
            if (this == this.layer.tile) {
                this.layer.events.triggerEvent("loadstart");
            }
        };
        tile.events.register("loadstart", tile, tile.onLoadStart);
        tile.onLoadEnd = function () {
            if (this == this.layer.tile) {
                this.layer.events.triggerEvent("tileloaded");
                this.layer.events.triggerEvent("loadend");
            }
        };
        tile.events.register("loadend", tile, tile.onLoadEnd);
        tile.events.register("unload", tile, tile.onLoadEnd);
    },
    removeTileMonitoringHooks: function (tile) {
        tile.unload();
        tile.events.un({
            "loadstart": tile.onLoadStart,
            "loadend": tile.onLoadEnd,
            "unload": tile.onLoadEnd,
            scope: tile
        });
    },
    onMapResize: function () {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.onMapResize.apply(this, arguments);
        } else {
            OpenLayers.Layer.Markers.prototype.onMapResize.apply(this, arguments);
        }
    },
    display: function () {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.display.apply(this, arguments);
        } else {
            OpenLayers.Layer.Markers.prototype.display.apply(this, arguments);
        }
    },
    mergeNewParams: function (newParams) {
        var upperParams = OpenLayers.Util.upperCaseObject(newParams);
        var newArguments = [upperParams];
        return OpenLayers.Layer.HTTPRequest.prototype.mergeNewParams.apply(this, newArguments);
    },
    clone: function (obj) {
        if (obj == null) {
            obj = new OpenLayers.Layer.WFS(this.name, this.url, this.params, this.getOptions());
        }
        if (this.vectorMode) {
            obj = OpenLayers.Layer.Vector.prototype.clone.apply(this, [obj]);
        } else {
            obj = OpenLayers.Layer.Markers.prototype.clone.apply(this, [obj]);
        }
        return obj;
    },
    getFullRequestString: function (newParams, altUrl) {
        var projectionCode = this.projection.getCode() || this.map.getProjection();
        this.params.SRS = (projectionCode == "none") ? null : projectionCode;
        return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments);
    },
    commit: function () {
        if (!this.writer) {
            var options = {};
            if (this.map && !this.projection.equals(this.map.getProjectionObject())) {
                options.externalProjection = this.projection;
                options.internalProjection = this.map.getProjectionObject();
            }
            this.writer = new OpenLayers.Format.WFS(options, this);
        }
        var data = this.writer.write(this.features);
        OpenLayers.Request.POST({
            url: this.url,
            data: data,
            success: this.commitSuccess,
            failure: this.commitFailure,
            scope: this
        });
    },
    commitSuccess: function (request) {
        var response = request.responseText;
        if (response.indexOf('SUCCESS') != -1) {
            this.commitReport(OpenLayers.i18n("commitSuccess", {
                'response': response
            }));
            for (var i = 0; i < this.features.length; i++) {
                this.features[i].state = null;
            }
        } else if (response.indexOf('FAILED') != -1 || response.indexOf('Exception') != -1) {
            this.commitReport(OpenLayers.i18n("commitFailed", {
                'response': response
            }));
        }
    },
    commitFailure: function (request) {},
    commitReport: function (string, response) {
        OpenLayers.Console.userError(string);
    },
    refresh: function () {
        if (this.tile) {
            if (this.vectorMode) {
                this.renderer.clear();
                this.features.length = 0;
            } else {
                this.clearMarkers();
                this.markers.length = 0;
            }
            this.tile.draw();
        }
    },
    getDataExtent: function () {
        var extent;
        if (this.vectorMode) {
            extent = OpenLayers.Layer.Vector.prototype.getDataExtent.apply(this);
        } else {
            extent = OpenLayers.Layer.Markers.prototype.getDataExtent.apply(this);
        }
        return extent;
    },
    setOpacity: function (opacity) {
        if (this.vectorMode) {
            OpenLayers.Layer.Vector.prototype.setOpacity.apply(this, [opacity]);
        } else {
            OpenLayers.Layer.Markers.prototype.setOpacity.apply(this, [opacity]);
        }
    },
    CLASS_NAME: "OpenLayers.Layer.WFS"
});
OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, {
    roundedCorner: true,
    roundedCornerColor: "darkblue",
    layerStates: null,
    layersDiv: null,
    baseLayersDiv: null,
    baseLayers: null,
    dataLbl: null,
    dataLayersDiv: null,
    dataLayers: null,
    minimizeDiv: null,
    maximizeDiv: null,
    ascending: true,
    initialize: function (options) {
        OpenLayers.Control.prototype.initialize.apply(this, arguments);
        this.layerStates = [];
    },
    destroy: function () {
        OpenLayers.Event.stopObservingElement(this.div);
        OpenLayers.Event.stopObservingElement(this.minimizeDiv);
        OpenLayers.Event.stopObservingElement(this.maximizeDiv);
        this.clearLayersArray("base");
        this.clearLayersArray("data");
        this.map.events.un({
            "addlayer": this.redraw,
            "changelayer": this.redraw,
            "removelayer": this.redraw,
            "changebaselayer": this.redraw,
            scope: this
        });
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    setMap: function (map) {
        OpenLayers.Control.prototype.setMap.apply(this, arguments);
        this.map.events.on({
            "addlayer": this.redraw,
            "changelayer": this.redraw,
            "removelayer": this.redraw,
            "changebaselayer": this.redraw,
            scope: this
        });
    },
    draw: function () {
        OpenLayers.Control.prototype.draw.apply(this);
        this.loadContents();
        if (!this.outsideViewport) {
            this.minimizeControl();
        }
        this.redraw();
        return this.div;
    },
    clearLayersArray: function (layersType) {
        var layers = this[layersType + "Layers"];
        if (layers) {
            for (var i = 0, len = layers.length; i < len; i++) {
                var layer = layers[i];
                OpenLayers.Event.stopObservingElement(layer.inputElem);
                OpenLayers.Event.stopObservingElement(layer.labelSpan);
            }
        }
        this[layersType + "LayersDiv"].innerHTML = "";
        this[layersType + "Layers"] = [];
    },
    checkRedraw: function () {
        var redraw = false;
        if (!this.layerStates.length || (this.map.layers.length != this.layerStates.length)) {
            redraw = true;
        } else {
            for (var i = 0, len = this.layerStates.length; i < len; i++) {
                var layerState = this.layerStates[i];
                var layer = this.map.layers[i];
                if ((layerState.name != layer.name) || (layerState.inRange != layer.inRange) || (layerState.id != layer.id) || (layerState.visibility != layer.visibility)) {
                    redraw = true;
                    break;
                }
            }
        }
        return redraw;
    },
    redraw: function () {
        if (!this.checkRedraw()) {
            return this.div;
        }
        this.clearLayersArray("base");
        this.clearLayersArray("data");
        var containsOverlays = false;
        var containsBaseLayers = false;
        var len = this.map.layers.length;
        this.layerStates = new Array(len);
        for (var i = 0; i < len; i++) {
            var layer = this.map.layers[i];
            this.layerStates[i] = {
                'name': layer.name,
                'visibility': layer.visibility,
                'inRange': layer.inRange,
                'id': layer.id
            };
        }
        var layers = this.map.layers.slice();
        if (!this.ascending) {
            layers.reverse();
        }
        for (var i = 0, len = layers.length; i < len; i++) {
            var layer = layers[i];
            var baseLayer = layer.isBaseLayer;
            if (layer.displayInLayerSwitcher) {
                if (baseLayer) {
                    containsBaseLayers = true;
                } else {
                    containsOverlays = true;
                }
                var checked = (baseLayer) ? (layer == this.map.baseLayer) : layer.getVisibility();
                var inputElem = document.createElement("input");
                inputElem.id = this.id + "_input_" + layer.name;
                inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name;
                inputElem.type = (baseLayer) ? "radio" : "checkbox";
                inputElem.value = layer.name;
                inputElem.checked = checked;
                inputElem.defaultChecked = checked;
                if (!baseLayer && !layer.inRange) {
                    inputElem.disabled = true;
                }
                var context = {
                    'inputElem': inputElem,
                    'layer': layer,
                    'layerSwitcher': this
                };
                OpenLayers.Event.observe(inputElem, "mouseup", OpenLayers.Function.bindAsEventListener(this.onInputClick, context));
                var labelSpan = document.createElement("span");
                OpenLayers.Element.addClass(labelSpan, "labelSpan");
                if (!baseLayer && !layer.inRange) {
                    labelSpan.style.color = "gray";
                }
                labelSpan.innerHTML = layer.name;
                labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : "baseline";
                OpenLayers.Event.observe(labelSpan, "click", OpenLayers.Function.bindAsEventListener(this.onInputClick, context));
                var br = document.createElement("br");
                var groupArray = (baseLayer) ? this.baseLayers : this.dataLayers;
                groupArray.push({
                    'layer': layer,
                    'inputElem': inputElem,
                    'labelSpan': labelSpan
                });
                var groupDiv = (baseLayer) ? this.baseLayersDiv : this.dataLayersDiv;
                groupDiv.appendChild(inputElem);
                groupDiv.appendChild(labelSpan);
                groupDiv.appendChild(br);
            }
        }
        this.dataLbl.style.display = (containsOverlays) ? "" : "none";
        this.baseLbl.style.display = (containsBaseLayers) ? "" : "none";
        return this.div;
    },
    onInputClick: function (e) {
        if (!this.inputElem.disabled) {
            if (this.inputElem.type == "radio") {
                this.inputElem.checked = true;
                this.layer.map.setBaseLayer(this.layer);
            } else {
                this.inputElem.checked = !this.inputElem.checked;
                this.layerSwitcher.updateMap();
            }
        }
        OpenLayers.Event.stop(e);
    },
    onLayerClick: function (e) {
        this.updateMap();
    },
    updateMap: function () {
        for (var i = 0, len = this.baseLayers.length; i < len; i++) {
            var layerEntry = this.baseLayers[i];
            if (layerEntry.inputElem.checked) {
                this.map.setBaseLayer(layerEntry.layer, false);
            }
        }
        for (var i = 0, len = this.dataLayers.length; i < len; i++) {
            var layerEntry = this.dataLayers[i];
            layerEntry.layer.setVisibility(layerEntry.inputElem.checked);
        }
    },
    maximizeControl: function (e) {
        this.div.style.width = "";
        this.div.style.height = "";
        this.showControls(false);
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },
    minimizeControl: function (e) {
        this.div.style.width = "0px";
        this.div.style.height = "0px";
        this.showControls(true);
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },
    showControls: function (minimize) {
        this.maximizeDiv.style.display = minimize ? "" : "none";
        this.minimizeDiv.style.display = minimize ? "none" : "";
        this.layersDiv.style.display = minimize ? "none" : "";
    },
    loadContents: function () {
        OpenLayers.Event.observe(this.div, "mouseup", OpenLayers.Function.bindAsEventListener(this.mouseUp, this));
        OpenLayers.Event.observe(this.div, "click", this.ignoreEvent);
        OpenLayers.Event.observe(this.div, "mousedown", OpenLayers.Function.bindAsEventListener(this.mouseDown, this));
        OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);
        this.layersDiv = document.createElement("div");
        this.layersDiv.id = this.id + "_layersDiv";
        OpenLayers.Element.addClass(this.layersDiv, "layersDiv");
        this.baseLbl = document.createElement("div");
        this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer");
        OpenLayers.Element.addClass(this.baseLbl, "baseLbl");
        this.baseLayersDiv = document.createElement("div");
        OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv");
        this.dataLbl = document.createElement("div");
        this.dataLbl.innerHTML = OpenLayers.i18n("Overlays");
        OpenLayers.Element.addClass(this.dataLbl, "dataLbl");
        this.dataLayersDiv = document.createElement("div");
        OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv");
        if (this.ascending) {
            this.layersDiv.appendChild(this.baseLbl);
            this.layersDiv.appendChild(this.baseLayersDiv);
            this.layersDiv.appendChild(this.dataLbl);
            this.layersDiv.appendChild(this.dataLayersDiv);
        } else {
            this.layersDiv.appendChild(this.dataLbl);
            this.layersDiv.appendChild(this.dataLayersDiv);
            this.layersDiv.appendChild(this.baseLbl);
            this.layersDiv.appendChild(this.baseLayersDiv);
        }
        this.div.appendChild(this.layersDiv);
        if (this.roundedCorner) {
            OpenLayers.Rico.Corner.round(this.div, {
                corners: "tl bl",
                bgColor: "transparent",
                color: this.roundedCornerColor,
                blend: false
            });
            OpenLayers.Rico.Corner.changeOpacity(this.layersDiv, 0.75);
        }
        var imgLocation = OpenLayers.Util.getImagesLocation();
        var sz = new OpenLayers.Size(18, 18);
        var img = imgLocation + 'layer-switcher-maximize.png';
        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv", null, sz, img, "absolute");
        OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv");
        this.maximizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.maximizeDiv, "click", OpenLayers.Function.bindAsEventListener(this.maximizeControl, this));
        this.div.appendChild(this.maximizeDiv);
        var img = imgLocation + 'layer-switcher-minimize.png';
        var sz = new OpenLayers.Size(18, 18);
        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv", null, sz, img, "absolute");
        OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv");
        this.minimizeDiv.style.display = "none";
        OpenLayers.Event.observe(this.minimizeDiv, "click", OpenLayers.Function.bindAsEventListener(this.minimizeControl, this));
        this.div.appendChild(this.minimizeDiv);
    },
    ignoreEvent: function (evt) {
        OpenLayers.Event.stop(evt);
    },
    mouseDown: function (evt) {
        this.isMouseDown = true;
        this.ignoreEvent(evt);
    },
    mouseUp: function (evt) {
        if (this.isMouseDown) {
            this.isMouseDown = false;
            this.ignoreEvent(evt);
        }
    },
    CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
});
OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, {
    layer: null,
    wfsns: "http://www.opengis.net/wfs",
    ogcns: "http://www.opengis.net/ogc",
    initialize: function (options, layer) {
        OpenLayers.Format.GML.prototype.initialize.apply(this, [options]);
        this.layer = layer;
        if (this.layer.featureNS) {
            this.featureNS = this.layer.featureNS;
        }
        if (this.layer.options.geometry_column) {
            this.geometryName = this.layer.options.geometry_column;
        }
        if (this.layer.options.typename) {
            this.featureName = this.layer.options.typename;
        }
    },
    write: function (features) {
        var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction');
        transaction.setAttribute("version", "1.0.0");
        transaction.setAttribute("service", "WFS");
        for (var i = 0; i < features.length; i++) {
            switch (features[i].state) {
            case OpenLayers.State.INSERT:
                transaction.appendChild(this.insert(features[i]));
                break;
            case OpenLayers.State.UPDATE:
                transaction.appendChild(this.update(features[i]));
                break;
            case OpenLayers.State.DELETE:
                transaction.appendChild(this.remove(features[i]));
                break;
            }
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]);
    },
    createFeatureXML: function (feature) {
        var geometryNode = this.buildGeometryNode(feature.geometry);
        var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName);
        geomContainer.appendChild(geometryNode);
        var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName);
        featureContainer.appendChild(geomContainer);
        for (var attr in feature.attributes) {
            var attrText = this.createTextNode(feature.attributes[attr]);
            var nodename = attr;
            if (attr.search(":") != -1) {
                nodename = attr.split(":")[1];
            }
            var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename);
            attrContainer.appendChild(attrText);
            featureContainer.appendChild(attrContainer);
        }
        return featureContainer;
    },
    insert: function (feature) {
        var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert');
        insertNode.appendChild(this.createFeatureXML(feature));
        return insertNode;
    },
    update: function (feature) {
        if (!feature.fid) {
            OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
        }
        var updateNode = this.createElementNS(this.wfsns, 'wfs:Update');
        updateNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName);
        updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
        var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
        var nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
        var txtNode = this.createTextNode(this.geometryName);
        nameNode.appendChild(txtNode);
        propertyNode.appendChild(nameNode);
        var valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
        var geometryNode = this.buildGeometryNode(feature.geometry);
        if (feature.layer) {
            geometryNode.setAttribute("srsName", feature.layer.projection.getCode());
        }
        valueNode.appendChild(geometryNode);
        propertyNode.appendChild(valueNode);
        updateNode.appendChild(propertyNode);
        for (var propName in feature.attributes) {
            propertyNode = this.createElementNS(this.wfsns, 'wfs:Property');
            nameNode = this.createElementNS(this.wfsns, 'wfs:Name');
            nameNode.appendChild(this.createTextNode(propName));
            propertyNode.appendChild(nameNode);
            valueNode = this.createElementNS(this.wfsns, 'wfs:Value');
            valueNode.appendChild(this.createTextNode(feature.attributes[propName]));
            propertyNode.appendChild(valueNode);
            updateNode.appendChild(propertyNode);
        }
        var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
        var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
        filterIdNode.setAttribute("fid", feature.fid);
        filterNode.appendChild(filterIdNode);
        updateNode.appendChild(filterNode);
        return updateNode;
    },
    remove: function (feature) {
        if (!feature.fid) {
            OpenLayers.Console.userError(OpenLayers.i18n("noFID"));
            return false;
        }
        var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete');
        deleteNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName);
        deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS);
        var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter');
        var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId');
        filterIdNode.setAttribute("fid", feature.fid);
        filterNode.appendChild(filterIdNode);
        deleteNode.appendChild(filterNode);
        return deleteNode;
    },
    destroy: function () {
        this.layer = null;
    },
    CLASS_NAME: "OpenLayers.Format.WFS"
});
OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, {
    namespaces: {
        atom: "http://www.w3.org/2005/Atom",
        georss: "http://www.georss.org/georss"
    },
    feedTitle: "untitled",
    defaultEntryTitle: "untitled",
    gmlParser: null,
    xy: false,
    read: function (doc) {
        if (typeof doc == "string") {
            doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]);
        }
        return this.parseFeatures(doc);
    },
    write: function (features) {
        var doc;
        if (OpenLayers.Util.isArray(features)) {
            doc = this.createElementNSPlus("atom:feed");
            doc.appendChild(this.createElementNSPlus("atom:title", {
                value: this.feedTitle
            }));
            for (var i = 0, ii = features.length; i < ii; i++) {
                doc.appendChild(this.buildEntryNode(features[i]));
            }
        } else {
            doc = this.buildEntryNode(features);
        }
        return OpenLayers.Format.XML.prototype.write.apply(this, [doc]);
    },
    buildContentNode: function (content) {
        var node = this.createElementNSPlus("atom:content", {
            attributes: {
                type: content.type || null
            }
        });
        if (content.src) {
            node.setAttribute("src", content.src);
        } else {
            if (content.type == "text" || content.type == null) {
                node.appendChild(this.createTextNode(content.value));
            } else if (content.type == "html") {
                if (typeof content.value != "string") {
                    throw "HTML content must be in form of an escaped string";
                }
                node.appendChild(this.createTextNode(content.value));
            } else if (content.type == "xhtml") {
                node.appendChild(content.value);
            } else if (content.type == "xhtml" || content.type.match(/(\+|\/)xml$/)) {
                node.appendChild(content.value);
            } else {
                node.appendChild(this.createTextNode(content.value));
            }
        }
        return node;
    },
    buildEntryNode: function (feature) {
        var attrib = feature.attributes;
        var atomAttrib = attrib.atom || {};
        var entryNode = this.createElementNSPlus("atom:entry");
        if (atomAttrib.authors) {
            var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? atomAttrib.authors : [atomAttrib.authors];
            for (var i = 0, ii = authors.length; i < ii; i++) {
                entryNode.appendChild(this.buildPersonConstructNode("author", authors[i]));
            }
        }
        if (atomAttrib.categories) {
            var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? atomAttrib.categories : [atomAttrib.categories];
            var category;
            for (var i = 0, ii = categories.length; i < ii; i++) {
                category = categories[i];
                entryNode.appendChild(this.createElementNSPlus("atom:category", {
                    attributes: {
                        term: category.term,
                        scheme: category.scheme || null,
                        label: category.label || null
                    }
                }));
            }
        }
        if (atomAttrib.content) {
            entryNode.appendChild(this.buildContentNode(atomAttrib.content));
        }
        if (atomAttrib.contributors) {
            var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? atomAttrib.contributors : [atomAttrib.contributors];
            for (var i = 0, ii = contributors.length; i < ii; i++) {
                entryNode.appendChild(this.buildPersonConstructNode("contributor", contributors[i]));
            }
        }
        if (feature.fid) {
            entryNode.appendChild(this.createElementNSPlus("atom:id", {
                value: feature.fid
            }));
        }
        if (atomAttrib.links) {
            var links = OpenLayers.Util.isArray(atomAttrib.links) ? atomAttrib.links : [atomAttrib.links];
            var link;
            for (var i = 0, ii = links.length; i < ii; i++) {
                link = links[i];
                entryNode.appendChild(this.createElementNSPlus("atom:link", {
                    attributes: {
                        href: link.href,
                        rel: link.rel || null,
                        type: link.type || null,
                        hreflang: link.hreflang || null,
                        title: link.title || null,
                        length: link.length || null
                    }
                }));
            }
        }
        if (atomAttrib.published) {
            entryNode.appendChild(this.createElementNSPlus("atom:published", {
                value: atomAttrib.published
            }));
        }
        if (atomAttrib.rights) {
            entryNode.appendChild(this.createElementNSPlus("atom:rights", {
                value: atomAttrib.rights
            }));
        }
        if (atomAttrib.summary || attrib.description) {
            entryNode.appendChild(this.createElementNSPlus("atom:summary", {
                value: atomAttrib.summary || attrib.description
            }));
        }
        entryNode.appendChild(this.createElementNSPlus("atom:title", {
            value: atomAttrib.title || attrib.title || this.defaultEntryTitle
        }));
        if (atomAttrib.updated) {
            entryNode.appendChild(this.createElementNSPlus("atom:updated", {
                value: atomAttrib.updated
            }));
        }
        if (feature.geometry) {
            var whereNode = this.createElementNSPlus("georss:where");
            whereNode.appendChild(this.buildGeometryNode(feature.geometry));
            entryNode.appendChild(whereNode);
        }
        return entryNode;
    },
    initGmlParser: function () {
        this.gmlParser = new OpenLayers.Format.GML.v3({
            xy: this.xy,
            featureNS: "http://example.com#feature",
            internalProjection: this.internalProjection,
            externalProjection: this.externalProjection
        });
    },
    buildGeometryNode: function (geometry) {
        if (!this.gmlParser) {
            this.initGmlParser();
        }
        var node = this.gmlParser.writeNode("feature:_geometry", geometry);
        return node.firstChild;
    },
    buildPersonConstructNode: function (name, value) {
        var oNames = ["uri", "email"];
        var personNode = this.createElementNSPlus("atom:" + name);
        personNode.appendChild(this.createElementNSPlus("atom:name", {
            value: value.name
        }));
        for (var i = 0, ii = oNames.length; i < ii; i++) {
            if (value[oNames[i]]) {
                personNode.appendChild(this.createElementNSPlus("atom:" + oNames[i], {
                    value: value[oNames[i]]
                }));
            }
        }
        return personNode;
    },
    getFirstChildValue: function (node, nsuri, name, def) {
        var value;
        var nodes = this.getElementsByTagNameNS(node, nsuri, name);
        if (nodes && nodes.length > 0) {
            value = this.getChildValue(nodes[0], def);
        } else {
            value = def;
        }
        return value;
    },
    parseFeature: function (node) {
        var atomAttrib = {};
        var value = null;
        var nodes = null;
        var attval = null;
        var atomns = this.namespaces.atom;
        this.parsePersonConstructs(node, "author", atomAttrib);
        nodes = this.getElementsByTagNameNS(node, atomns, "category");
        if (nodes.length > 0) {
            atomAttrib.categories = [];
        }
        for (var i = 0, ii = nodes.length; i < ii; i++) {
            value = {};
            value.term = nodes[i].getAttribute("term");
            attval = nodes[i].getAttribute("scheme");
            if (attval) {
                value.scheme = attval;
            }
            attval = nodes[i].getAttribute("label");
            if (attval) {
                value.label = attval;
            }
            atomAttrib.categories.push(value);
        }
        nodes = this.getElementsByTagNameNS(node, atomns, "content");
        if (nodes.length > 0) {
            value = {};
            attval = nodes[0].getAttribute("type");
            if (attval) {
                value.type = attval;
            }
            attval = nodes[0].getAttribute("src");
            if (attval) {
                value.src = attval;
            } else {
                if (value.type == "text" || value.type == "html" || value.type == null) {
                    value.value = this.getFirstChildValue(node, atomns, "content", null);
                } else if (value.type == "xhtml" || value.type.match(/(\+|\/)xml$/)) {
                    value.value = this.getChildEl(nodes[0]);
                } else {
                    value.value = this.getFirstChildValue(node, atomns, "content", null);
                }
                atomAttrib.content = value;
            }
        }
        this.parsePersonConstructs(node, "contributor", atomAttrib);
        atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null);
        nodes = this.getElementsByTagNameNS(node, atomns, "link");
        if (nodes.length > 0) {
            atomAttrib.links = new Array(nodes.length);
        }
        var oAtts = ["rel", "type", "hreflang", "title", "length"];
        for (var i = 0, ii = nodes.length; i < ii; i++) {
            value = {};
            value.href = nodes[i].getAttribute("href");
            for (var j = 0, jj = oAtts.length; j < jj; j++) {
                attval = nodes[i].getAttribute(oAtts[j]);
                if (attval) {
                    value[oAtts[j]] = attval;
                }
            }
            atomAttrib.links[i] = value;
        }
        value = this.getFirstChildValue(node, atomns, "published", null);
        if (value) {
            atomAttrib.published = value;
        }
        value = this.getFirstChildValue(node, atomns, "rights", null);
        if (value) {
            atomAttrib.rights = value;
        }
        value = this.getFirstChildValue(node, atomns, "summary", null);
        if (value) {
            atomAttrib.summary = value;
        }
        atomAttrib.title = this.getFirstChildValue(node, atomns, "title", null);
        atomAttrib.updated = this.getFirstChildValue(node, atomns, "updated", null);
        var featureAttrib = {
            title: atomAttrib.title,
            description: atomAttrib.summary,
            atom: atomAttrib
        };
        var geometry = this.parseLocations(node)[0];
        var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib);
        feature.fid = atomAttrib.id;
        return feature;
    },
    parseFeatures: function (node) {
        var features = [];
        var entries = this.getElementsByTagNameNS(node, this.namespaces.atom, "entry");
        if (entries.length == 0) {
            entries = [node];
        }
        for (var i = 0, ii = entries.length; i < ii; i++) {
            features.push(this.parseFeature(entries[i]));
        }
        return features;
    },
    parseLocations: function (node) {
        var georssns = this.namespaces.georss;
        var locations = {
            components: []
        };
        var where = this.getElementsByTagNameNS(node, georssns, "where");
        if (where && where.length > 0) {
            if (!this.gmlParser) {
                this.initGmlParser();
            }
            for (var i = 0, ii = where.length; i < ii; i++) {
                this.gmlParser.readChildNodes(where[i], locations);
            }
        }
        var components = locations.components;
        var point = this.getElementsByTagNameNS(node, georssns, "point");
        if (point && point.length > 0) {
            for (var i = 0, ii = point.length; i < ii; i++) {
                var xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\s+/);
                if (xy.length != 2) {
                    xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\s*,\s*/);
                }
                components.push(new OpenLayers.Geometry.Point(parseFloat(xy[1]), parseFloat(xy[0])));
            }
        }
        var line = this.getElementsByTagNameNS(node, georssns, "line");
        if (line && line.length > 0) {
            var coords;
            var p;
            var points;
            for (var i = 0, ii = line.length; i < ii; i++) {
                coords = OpenLayers.String.trim(line[i].firstChild.nodeValue).split(/\s+/);
                points = [];
                for (var j = 0, jj = coords.length; j < jj; j += 2) {
                    p = new OpenLayers.Geometry.Point(parseFloat(coords[j + 1]), parseFloat(coords[j]));
                    points.push(p);
                }
                components.push(new OpenLayers.Geometry.LineString(points));
            }
        }
        var polygon = this.getElementsByTagNameNS(node, georssns, "polygon");
        if (polygon && polygon.length > 0) {
            var coords;
            var p;
            var points;
            for (var i = 0, ii = polygon.length; i < ii; i++) {
                coords = OpenLayers.String.trim(polygon[i].firstChild.nodeValue).split(/\s+/);
                points = [];
                for (var j = 0, jj = coords.length; j < jj; j += 2) {
                    p = new OpenLayers.Geometry.Point(parseFloat(coords[j + 1]), parseFloat(coords[j]));
                    points.push(p);
                }
                components.push(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]));
            }
        }
        if (this.internalProjection && this.externalProjection) {
            for (var i = 0, ii = components.length; i < ii; i++) {
                if (components[i]) {
                    components[i].transform(this.externalProjection, this.internalProjection);
                }
            }
        }
        return components;
    },
    parsePersonConstructs: function (node, name, data) {
        var persons = [];
        var atomns = this.namespaces.atom;
        var nodes = this.getElementsByTagNameNS(node, atomns, name);
        var oAtts = ["uri", "email"];
        for (var i = 0, ii = nodes.length; i < ii; i++) {
            var value = {};
            value.name = this.getFirstChildValue(nodes[i], atomns, "name", null);
            for (var j = 0, jj = oAtts.length; j < jj; j++) {
                var attval = this.getFirstChildValue(nodes[i], atomns, oAtts[j], null);
                if (attval) {
                    value[oAtts[j]] = attval;
                }
            }
            persons.push(value);
        }
        if (persons.length > 0) {
            data[name + "s"] = persons;
        }
    },
    CLASS_NAME: "OpenLayers.Format.Atom"
});
OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, {
    autoActivate: true,
    slideFactor: 75,
    draw: function () {
        this.handler = new OpenLayers.Handler.Keyboard(this, {
            "keydown": this.defaultKeyPress
        });
    },
    defaultKeyPress: function (evt) {
        switch (evt.keyCode) {
        case OpenLayers.Event.KEY_LEFT:
            this.map.pan(-this.slideFactor, 0);
            break;
        case OpenLayers.Event.KEY_RIGHT:
            this.map.pan(this.slideFactor, 0);
            break;
        case OpenLayers.Event.KEY_UP:
            this.map.pan(0, -this.slideFactor);
            break;
        case OpenLayers.Event.KEY_DOWN:
            this.map.pan(0, this.slideFactor);
            break;
        case 33:
            var size = this.map.getSize();
            this.map.pan(0, -0.75 * size.h);
            break;
        case 34:
            var size = this.map.getSize();
            this.map.pan(0, 0.75 * size.h);
            break;
        case 35:
            var size = this.map.getSize();
            this.map.pan(0.75 * size.w, 0);
            break;
        case 36:
            var size = this.map.getSize();
            this.map.pan(-0.75 * size.w, 0);
            break;
        case 43:
        case 61:
        case 187:
        case 107:
            this.map.zoomIn();
            break;
        case 45:
        case 109:
        case 189:
        case 95:
            this.map.zoomOut();
            break;
        }
    },
    CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
});
OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class(OpenLayers.Format.WMC.v1, {
    VERSION: "1.0.0",
    schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd",
    initialize: function (options) {
        OpenLayers.Format.WMC.v1.prototype.initialize.apply(this, [options]);
    },
    read_wmc_SRS: function (layerContext, node) {
        var srs = this.getChildValue(node);
        if (typeof layerContext.projections != "object") {
            layerContext.projections = {};
        }
        var values = srs.split(/ +/);
        for (var i = 0, len = values.length; i < len; i++) {
            layerContext.projections[values[i]] = true;
        }
    },
    write_wmc_Layer: function (context) {
        var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this, [context]);
        if (context.srs) {
            var projections = [];
            for (var name in context.srs) {
                projections.push(name);
            }
            node.appendChild(this.createElementDefaultNS("SRS", projections.join(" ")));
        }
        node.appendChild(this.write_wmc_FormatList(context));
        node.appendChild(this.write_wmc_StyleList(context));
        if (context.dimensions) {
            node.appendChild(this.write_wmc_DimensionList(context));
        }
        node.appendChild(this.write_wmc_LayerExtension(context));
    },
    CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0"
});
OpenLayers.Feature.WFS = OpenLayers.Class(OpenLayers.Feature, {
    initialize: function (layer, xmlNode) {
        var newArguments = arguments;
        var data = this.processXMLNode(xmlNode);
        newArguments = new Array(layer, data.lonlat, data);
        OpenLayers.Feature.prototype.initialize.apply(this, newArguments);
        this.createMarker();
        this.layer.addMarker(this.marker);
    },
    destroy: function () {
        if (this.marker != null) {
            this.layer.removeMarker(this.marker);
        }
        OpenLayers.Feature.prototype.destroy.apply(this, arguments);
    },
    processXMLNode: function (xmlNode) {
        var point = OpenLayers.Ajax.getElementsByTagNameNS(xmlNode, "http://www.opengis.net/gml", "gml", "Point");
        var text = OpenLayers.Util.getXmlNodeValue(OpenLayers.Ajax.getElementsByTagNameNS(point[0], "http://www.opengis.net/gml", "gml", "coordinates")[0]);
        var floats = text.split(",");
        return {
            lonlat: new OpenLayers.LonLat(parseFloat(floats[0]), parseFloat(floats[1])),
            id: null
        };
    },
    CLASS_NAME: "OpenLayers.Feature.WFS"
});
OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class(OpenLayers.Format.OWSCommon.v1_1_0, {
    version: "1.0.0",
    namespaces: {
        ows: "http://www.opengis.net/ows/1.1",
        wmts: "http://www.opengis.net/wmts/1.0",
        xlink: "http://www.w3.org/1999/xlink"
    },
    yx: null,
    defaultPrefix: "wmts",
    initialize: function (options) {
        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
        this.options = options;
        var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx);
        this.yx = OpenLayers.Util.extend(yx, this.yx);
    },
    read: function (data) {
        if (typeof data == "string") {
            data = OpenLayers.Format.XML.prototype.read.apply(this, [data]);
        }
        if (data && data.nodeType == 9) {
            data = data.documentElement;
        }
        var capabilities = {};
        this.readNode(data, capabilities);
        capabilities.version = this.version;
        return capabilities;
    },
    readers: {
        "wmts": {
            "Capabilities": function (node, obj) {
                this.readChildNodes(node, obj);
            },
            "Contents": function (node, obj) {
                obj.contents = {};
                obj.contents.layers = [];
                obj.contents.tileMatrixSets = {};
                this.readChildNodes(node, obj.contents);
            },
            "Layer": function (node, obj) {
                var layer = {
                    styles: [],
                    formats: [],
                    tileMatrixSetLinks: []
                };
                layer.layers = [];
                this.readChildNodes(node, layer);
                obj.layers.push(layer);
            },
            "Style": function (node, obj) {
                var style = {};
                style.isDefault = (node.getAttribute("isDefault") === "true");
                this.readChildNodes(node, style);
                obj.styles.push(style);
            },
            "Format": function (node, obj) {
                obj.formats.push(this.getChildValue(node));
            },
            "TileMatrixSetLink": function (node, obj) {
                var tileMatrixSetLink = {};
                this.readChildNodes(node, tileMatrixSetLink);
                obj.tileMatrixSetLinks.push(tileMatrixSetLink);
            },
            "TileMatrixSet": function (node, obj) {
                if (obj.layers) {
                    var tileMatrixSet = {
                        matrixIds: []
                    };
                    this.readChildNodes(node, tileMatrixSet);
                    obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet;
                } else {
                    obj.tileMatrixSet = this.getChildValue(node);
                }
            },
            "TileMatrix": function (node, obj) {
                var tileMatrix = {
                    supportedCRS: obj.supportedCRS
                };
                this.readChildNodes(node, tileMatrix);
                obj.matrixIds.push(tileMatrix);
            },
            "ScaleDenominator": function (node, obj) {
                obj.scaleDenominator = parseFloat(this.getChildValue(node));
            },
            "TopLeftCorner": function (node, obj) {
                var topLeftCorner = this.getChildValue(node);
                var coords = topLeftCorner.split(" ");
                var yx;
                if (obj.supportedCRS) {
                    var crs = obj.supportedCRS.replace(/urn:ogc:def:crs:(\w+):.+:(\w+)$/, "urn:ogc:def:crs:$1::$2");
                    yx = !! this.yx[crs];
                }
                if (yx) {
                    obj.topLeftCorner = new OpenLayers.LonLat(coords[1], coords[0]);
                } else {
                    obj.topLeftCorner = new OpenLayers.LonLat(coords[0], coords[1]);
                }
            },
            "TileWidth": function (node, obj) {
                obj.tileWidth = parseInt(this.getChildValue(node));
            },
            "TileHeight": function (node, obj) {
                obj.tileHeight = parseInt(this.getChildValue(node));
            },
            "MatrixWidth": function (node, obj) {
                obj.matrixWidth = parseInt(this.getChildValue(node));
            },
            "MatrixHeight": function (node, obj) {
                obj.matrixHeight = parseInt(this.getChildValue(node));
            },
            "ResourceURL": function (node, obj) {
                obj.resourceUrl = obj.resourceUrl || {};
                obj.resourceUrl[node.getAttribute("resourceType")] = {
                    format: node.getAttribute("format"),
                    template: node.getAttribute("template")
                };
            },
            "WSDL": function (node, obj) {
                obj.wsdl = {};
                obj.wsdl.href = node.getAttribute("xlink:href");
            },
            "ServiceMetadataURL": function (node, obj) {
                obj.serviceMetadataUrl = {};
                obj.serviceMetadataUrl.href = node.getAttribute("xlink:href");
            }
        },
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"]
    },
    CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0"
});